nitro_kit 0.2.0 → 0.3.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +6 -4
  4. data/app/components/nitro_kit/accordion.rb +68 -32
  5. data/app/components/nitro_kit/alert.rb +69 -0
  6. data/app/components/nitro_kit/avatar.rb +52 -0
  7. data/app/components/nitro_kit/badge.rb +46 -19
  8. data/app/components/nitro_kit/button.rb +99 -66
  9. data/app/components/nitro_kit/button_group.rb +18 -13
  10. data/app/components/nitro_kit/card.rb +49 -9
  11. data/app/components/nitro_kit/checkbox.rb +59 -41
  12. data/app/components/nitro_kit/checkbox_group.rb +38 -0
  13. data/app/components/nitro_kit/combobox.rb +138 -0
  14. data/app/components/nitro_kit/component.rb +45 -14
  15. data/app/components/nitro_kit/datepicker.rb +9 -0
  16. data/app/components/nitro_kit/dialog.rb +95 -0
  17. data/app/components/nitro_kit/dropdown.rb +112 -70
  18. data/app/components/nitro_kit/field.rb +221 -56
  19. data/app/components/nitro_kit/field_group.rb +12 -6
  20. data/app/components/nitro_kit/fieldset.rb +42 -7
  21. data/app/components/nitro_kit/form_builder.rb +45 -22
  22. data/app/components/nitro_kit/icon.rb +29 -8
  23. data/app/components/nitro_kit/input.rb +20 -10
  24. data/app/components/nitro_kit/label.rb +18 -5
  25. data/app/components/nitro_kit/pagination.rb +98 -0
  26. data/app/components/nitro_kit/radio_button.rb +28 -27
  27. data/app/components/nitro_kit/radio_button_group.rb +53 -0
  28. data/app/components/nitro_kit/select.rb +72 -0
  29. data/app/components/nitro_kit/switch.rb +49 -39
  30. data/app/components/nitro_kit/table.rb +56 -0
  31. data/app/components/nitro_kit/tabs.rb +98 -0
  32. data/app/components/nitro_kit/textarea.rb +26 -0
  33. data/app/components/nitro_kit/toast.rb +104 -0
  34. data/app/components/nitro_kit/tooltip.rb +53 -0
  35. data/app/helpers/nitro_kit/accordion_helper.rb +2 -0
  36. data/app/helpers/nitro_kit/alert_helper.rb +11 -0
  37. data/app/helpers/nitro_kit/avatar_helper.rb +9 -0
  38. data/app/helpers/nitro_kit/badge_helper.rb +3 -5
  39. data/app/helpers/nitro_kit/button_group_helper.rb +2 -0
  40. data/app/helpers/nitro_kit/button_helper.rb +37 -28
  41. data/app/helpers/nitro_kit/card_helper.rb +2 -0
  42. data/app/helpers/nitro_kit/checkbox_helper.rb +19 -16
  43. data/app/helpers/nitro_kit/combobox_helper.rb +9 -0
  44. data/app/helpers/nitro_kit/datepicker_helper.rb +9 -0
  45. data/app/helpers/nitro_kit/dialog_helper.rb +9 -0
  46. data/app/helpers/nitro_kit/dropdown_helper.rb +3 -1
  47. data/app/helpers/nitro_kit/field_group_helper.rb +9 -0
  48. data/app/helpers/nitro_kit/field_helper.rb +4 -2
  49. data/app/helpers/nitro_kit/fieldset_helper.rb +9 -0
  50. data/app/helpers/nitro_kit/form_helper.rb +13 -0
  51. data/app/helpers/nitro_kit/icon_helper.rb +3 -1
  52. data/app/helpers/nitro_kit/input_helper.rb +35 -0
  53. data/app/helpers/nitro_kit/label_helper.rb +12 -9
  54. data/app/helpers/nitro_kit/pagination_helper.rb +42 -0
  55. data/app/helpers/nitro_kit/radio_button_helper.rb +15 -12
  56. data/app/helpers/nitro_kit/select_helper.rb +24 -0
  57. data/app/helpers/nitro_kit/switch_helper.rb +4 -10
  58. data/app/helpers/nitro_kit/table_helper.rb +9 -0
  59. data/app/helpers/nitro_kit/tabs_helper.rb +9 -0
  60. data/app/helpers/nitro_kit/textarea_helper.rb +9 -0
  61. data/app/helpers/nitro_kit/toast_helper.rb +36 -0
  62. data/app/helpers/nitro_kit/tooltip_helper.rb +9 -0
  63. data/lib/generators/nitro_kit/add_generator.rb +38 -41
  64. data/lib/generators/nitro_kit/install_generator.rb +2 -1
  65. data/lib/nitro_kit/engine.rb +4 -0
  66. data/lib/nitro_kit/schema_builder.rb +90 -16
  67. data/lib/nitro_kit/version.rb +1 -1
  68. data/lib/nitro_kit.rb +39 -1
  69. data/lib/tasks/nitro_kit_tasks.rake +4 -0
  70. metadata +37 -10
  71. data/app/components/nitro_kit/radio_group.rb +0 -35
  72. data/app/helpers/application_helper.rb +0 -109
  73. data/lib/nitro_kit/railtie.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf2f2dc76bda92752d48ee5f90a51987dedc72d5a40e04567016abc906606092
4
- data.tar.gz: fa123bb3e2185331ec19cf7c79fb56e977b7a0daf301c6cdc7537be23ddc4d06
3
+ metadata.gz: 6d58e573f0c99860c6ede6591c772db3d2399c754a1cb2c25b65632c42ca8a00
4
+ data.tar.gz: 4796fae213b057ed86d3bcc693c504c7323abe020bcfa920713ea7d9a2e4438a
5
5
  SHA512:
6
- metadata.gz: 80004192401f6199f2dbe0aad196707028248584084615c4f7f85f5bf567e674eaa7ecdf9e487deb3886ba3b2899d1dc9153697b79b91aa6209c66350b0ff900
7
- data.tar.gz: fb0b55d92309a50f46a2f8c93641c57eedafc4c817434e812ff46259e2e98d3b66869b4a321e499f02b7e818bd97e88e8c98ba7c65a19c98a1e35254eaa0d8e3
6
+ metadata.gz: 2865c2a1a86d6cf786eff5a699ce98aabea646c44349ecb387369ee7ba3aaab1b928f28ea1fded788bc95ffeb06926aac017a4fe292e70bbc0e1ecfa7d0dc30f
7
+ data.tar.gz: 2f8f3fc20b0be34cc8fc07f01fba9ace962dad39926332a50bf65bec062b7d96398bdc790b708fb6c027f2193b542641c5127fd6920f417d359a448a82bff782
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Mikkel Malmberg
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
- # Add your own tasks in files placed in lib/tasks ending in .rake,
2
- # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
1
+ require "bundler/setup"
3
2
 
4
- require_relative "config/application"
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
5
 
6
- Rails.application.load_tasks
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -1,27 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NitroKit
2
4
  class Accordion < Component
3
- ITEM = "divide-y"
4
-
5
- TRIGGER = [
6
- "flex w-full items-center justify-between py-4 font-medium cursor-pointer",
7
- "group/accordion-trigger hover:underline transition-colors",
8
- "[&[aria-expanded='true']>svg]:rotate-180",
9
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
10
- ].freeze
11
-
12
- CONTENT = [
13
- "overflow-hidden transition-all duration-200",
14
- "[&[aria-hidden='true']]:h-0 [&[aria-hidden='false']]:h-auto"
15
- ].freeze
16
-
17
- ARROW = "transition-transform duration-200 text-muted-foreground group-hover/accordion-trigger:text-primary"
5
+ def initialize(**attrs)
6
+ super(
7
+ attrs,
8
+ class: item_class,
9
+ data: {controller: "nk--accordion"}
10
+ )
11
+ end
18
12
 
19
13
  def view_template
20
- div(
21
- **attrs,
22
- class: merge(ITEM, attrs[:class]),
23
- data: {controller: "nk--accordion"}
24
- ) do
14
+ div(**attrs) do
25
15
  yield
26
16
  end
27
17
  end
@@ -34,27 +24,73 @@ module NitroKit
34
24
 
35
25
  def trigger(text = nil, **attrs)
36
26
  button(
37
- type: "button",
38
- class: TRIGGER,
39
- data: {
40
- :action => "nk--accordion#toggle",
41
- :"nk--accordion-target" => "trigger"
42
- },
43
- aria: {expanded: "false", controls: "content"}
27
+ **mattr(
28
+ attrs,
29
+ type: "button",
30
+ class: trigger_class,
31
+ data: {
32
+ action: "nk--accordion#toggle",
33
+ nk__accordion_target: "trigger"
34
+ },
35
+ aria: {expanded: "false"}
36
+ )
44
37
  ) do
45
- div(**attrs) { text || yield }
46
- render(NitroKit::Icon.new(name: "chevron-down", class: ARROW))
38
+ block_given? ? yield : plain(text)
39
+ chevron_icon
47
40
  end
48
41
  end
49
42
 
50
43
  def content(**attrs)
51
44
  div(
52
- class: merge(CONTENT),
53
- data: {:"nk--accordion-target" => "content"},
54
- aria: {hidden: "true"}
45
+ **mattr(
46
+ attrs,
47
+ class: content_class,
48
+ data: {
49
+ nk__accordion_target: "content"
50
+ },
51
+ aria: {hidden: "true"}
52
+ )
55
53
  ) do
56
54
  div(class: "pb-4") { yield }
57
55
  end
58
56
  end
57
+
58
+ private
59
+
60
+ def item_class
61
+ "divide-y"
62
+ end
63
+
64
+ def trigger_class
65
+ [
66
+ "flex w-full items-center justify-between py-4 font-medium cursor-pointer",
67
+ "group/accordion-trigger hover:underline transition-colors",
68
+ "[&[aria-expanded='true']>svg]:rotate-180",
69
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
70
+ ]
71
+ end
72
+
73
+ def content_class
74
+ [
75
+ "overflow-hidden transition-all duration-200",
76
+ "[&[aria-hidden='true']]:h-0 [&[aria-hidden='false']]:h-auto"
77
+ ]
78
+ end
79
+
80
+ def arrow_class
81
+ "transition-transform duration-200 text-muted-foreground group-hover/accordion-trigger:text-primary"
82
+ end
83
+
84
+ def chevron_icon
85
+ svg(
86
+ class: "transition-transform duration-200 size-4 self-center place-self-end mr-2 pointer-events-none text-muted-foreground group-hover/accordion-trigger:text-primary",
87
+ viewbox: "0 0 24 24",
88
+ fill: "none",
89
+ stroke: "currentColor",
90
+ stroke_width: 1
91
+ ) do |svg|
92
+ svg.path(d: "m6 9 6 6 6-6")
93
+ end
94
+ end
59
95
  end
60
96
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NitroKit
4
+ class Alert < Component
5
+ VARIANTS = %i[default warning error success]
6
+
7
+ def initialize(variant: :default, **attrs)
8
+ @variant = variant
9
+
10
+ super(
11
+ attrs,
12
+ role: "alert",
13
+ class: [base_class, variant_class]
14
+ )
15
+ end
16
+
17
+ attr_reader :variant
18
+
19
+ def view_template
20
+ div(**attrs) do
21
+ yield
22
+ end
23
+ end
24
+
25
+ def title(text = nil, **attrs, &block)
26
+ h5(**mattr(attrs, class: title_class)) do
27
+ text_or_block(text, &block)
28
+ end
29
+ end
30
+
31
+ def description(text = nil, **attrs, &block)
32
+ div(**mattr(attrs, class: description_class)) do
33
+ text_or_block(text, &block)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def base_class
40
+ [
41
+ "relative border w-full rounded-md p-5 text-sm space-y-2",
42
+ "[&>svg~*]:pl-8 [&>svg]:absolute [&>svg]:top-5 [&>svg]:left-5"
43
+ ]
44
+ end
45
+
46
+ def variant_class
47
+ case variant
48
+ when :default
49
+ "border-border bg-background text-foreground"
50
+ when :warning
51
+ "bg-yellow-300/20 dark:bg-yellow-300/20 text-yellow-900 dark:text-yellow-100 border-yellow-500/80 dark:border-yellow-400/50"
52
+ when :success
53
+ "bg-green-300/20 dark:bg-green-300/20 text-green-900 dark:text-green-100 border-green-500/80 dark:border-green-400/50"
54
+ when :error
55
+ "bg-red-300/20 dark:bg-red-300/20 text-red-900 dark:text-red-100 border-red-400/80 dark:border-red-400/50"
56
+ else
57
+ raise ArgumentError, "Invalid variant: #{variant}"
58
+ end
59
+ end
60
+
61
+ def title_class
62
+ "font-medium text-lg leading-5"
63
+ end
64
+
65
+ def description_class
66
+ ""
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NitroKit
4
+ class Avatar < Component
5
+ include ActionView::Helpers::AssetUrlHelper
6
+
7
+ def initialize(src_arg = nil, src: nil, size: :md, **attrs)
8
+ @src = src_arg || src
9
+ @size = size
10
+
11
+ super(
12
+ attrs,
13
+ class: [container_class, size_classes]
14
+ )
15
+ end
16
+
17
+ attr_reader :src, :size
18
+
19
+ def view_template(&block)
20
+ div(**attrs) do
21
+ image
22
+ end
23
+ end
24
+
25
+ def image
26
+ helpers.image_tag(src, class: image_class)
27
+ end
28
+
29
+ private
30
+
31
+ def size_classes
32
+ case size
33
+ when :sm
34
+ "size-8"
35
+ when :md
36
+ "size-12"
37
+ when :lg
38
+ "size-16"
39
+ else
40
+ raise ArgumentError, "Invalid size: #{size}"
41
+ end
42
+ end
43
+
44
+ def container_class
45
+ "inline-flex overflow-hidden rounded-full"
46
+ end
47
+
48
+ def image_class
49
+ "block size-full bg-muted"
50
+ end
51
+ end
52
+ end
@@ -1,31 +1,58 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NitroKit
2
4
  class Badge < Component
3
- BADGE = "inline-flex items-center gap-x-1.5 rounded-md font-medium"
5
+ VARIANTS = %i[default outline]
4
6
 
5
- VARIANTS = {
6
- default: "border border-transparent bg-zinc-200 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300",
7
- outline: "border"
8
- }
7
+ def initialize(text = nil, variant: :default, size: :md, **attrs)
8
+ @text = text
9
+ @variant = variant
10
+ @size = size
9
11
 
10
- SIZES = {
11
- sm: "text-xs px-1.5 py-0.5",
12
- md: "text-sm px-2 py-0.5"
13
- }
12
+ super(
13
+ attrs,
14
+ class: [
15
+ base_class,
16
+ variant_class,
17
+ size_class
18
+ ]
19
+ )
20
+ end
14
21
 
15
- def initialize(variant: :default, size: :md, **attrs)
16
- super(**attrs)
22
+ attr_reader :text, :variant, :size
17
23
 
18
- @variant = variant
19
- @size = size
24
+ def view_template(&block)
25
+ span(**attrs) do
26
+ text_or_block(text, &block)
27
+ end
20
28
  end
21
29
 
22
- attr_reader :variant, :size
30
+ private
31
+
32
+ def base_class
33
+ "inline-flex items-center gap-x-1.5 rounded-md font-medium"
34
+ end
35
+
36
+ def variant_class
37
+ case variant
38
+ when :default
39
+ "bg-zinc-200 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"
40
+ when :outline
41
+ "border"
42
+ else
43
+ raise ArgumentError, "Invalid variant: #{variant}"
44
+ end
45
+ end
23
46
 
24
- def view_template
25
- span(
26
- **attrs,
27
- class: merge(BADGE, VARIANTS[variant], SIZES[size], attrs[:class])
28
- ) { yield }
47
+ def size_class
48
+ case size
49
+ when :sm
50
+ "text-xs px-1.5 py-0.5"
51
+ when :md
52
+ "text-sm px-2 py-0.5"
53
+ else
54
+ raise ArgumentError, "Invalid size: #{size}"
55
+ end
29
56
  end
30
57
  end
31
58
  end
@@ -1,88 +1,51 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NitroKit
2
4
  class Button < Component
3
- BASE = [
4
- "inline-flex items-center cursor-pointer shrink-0 justify-center rounded-md border gap-2 font-medium",
5
- # Disabled
6
- "disabled:opacity-70 disabled:pointer-events-none",
7
- # Focus
8
- "focus:outline-none focus:ring-[3px] focus:ring-offset-2 focus:ring-ring ring-offset-background",
9
- # Icon
10
- "[&_svg]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
11
- # If icon only, make square
12
- "[&_svg:first-child:last-child]:-mx-2"
13
- ].freeze
14
-
15
- VARIANTS = {
16
- default: [
17
- "bg-background text-foreground",
18
- "hover:bg-zinc-50 dark:hover:bg-zinc-900"
19
- ],
20
- primary: [
21
- "bg-primary text-white dark:text-zinc-950 border-primary",
22
- "hover:bg-primary/90 dark:hover:bg-primary/90"
23
- ],
24
- destructive: [
25
- "bg-destructive text-white border-destructive",
26
- "hover:bg-destructive/90 dark:hover:bg-destructive/90",
27
- "disabled:text-white/80"
28
- ],
29
- ghost: [
30
- "bg-transparent text-foreground border-transparent",
31
- "hover:bg-zinc-100 dark:hover:bg-zinc-900",
32
- "disabled:text-muted-foreground"
33
- ]
34
- }.freeze
35
-
36
- SIZES = {
37
- base: "px-4 h-9",
38
- sm: "px-2.5 h-7 text-sm",
39
- xs: "px-1.5 h-6 text-xs"
40
- }.freeze
5
+ VARIANTS = %i[default primary destructive ghost]
41
6
 
42
7
  def initialize(
8
+ text = nil,
43
9
  href: nil,
10
+ variant: :default,
11
+ size: :md,
44
12
  icon: nil,
45
13
  icon_right: nil,
46
- size: :base,
47
- type: :button,
48
- variant: :default,
49
14
  **attrs
50
15
  )
51
- super(**attrs)
52
-
16
+ @text = text
53
17
  @href = href
54
18
  @icon = icon
55
19
  @icon_right = icon_right
56
20
  @size = size
57
- @type = type
58
21
  @variant = variant
22
+
23
+ super(
24
+ attrs,
25
+ class: [
26
+ base_class,
27
+ variant_class,
28
+ size_class
29
+ ]
30
+ )
59
31
  end
60
32
 
61
33
  attr_reader(
34
+ :text,
62
35
  :href,
63
36
  :icon,
64
37
  :icon_right,
65
38
  :size,
66
- :type,
67
39
  :variant
68
40
  )
69
41
 
70
42
  def view_template(&block)
71
- class_list = merge(
72
- [
73
- BASE,
74
- VARIANTS[variant],
75
- SIZES[size],
76
- attrs[:class]
77
- ]
78
- )
79
-
80
43
  if href
81
- a(href:, **attrs, class: class_list) do
44
+ a(href:, **attrs) do
82
45
  contents(&block)
83
46
  end
84
47
  else
85
- button(type:, **attrs, class: class_list) do
48
+ button(type: :button, **attrs) do
86
49
  contents(&block)
87
50
  end
88
51
  end
@@ -90,19 +53,89 @@ module NitroKit
90
53
 
91
54
  private
92
55
 
93
- def contents
94
- text = safe(capture { yield })
95
- has_text = text.to_s.present?
56
+ def contents(&block)
57
+ has_content = text.present? || block_given?
96
58
 
97
- if !icon && has_text && !icon_right
98
- return text
99
- elsif icon && !has_text && !icon_right
100
- return render(Icon.new(name: icon))
59
+ if !has_content
60
+ return render(Icon.new(icon))
101
61
  end
102
62
 
103
- render(Icon.new(name: icon)) if icon
104
- span { text }
105
- render(Icon.new(name: icon_right)) if icon_right
63
+ render(Icon.new(icon)) if icon
64
+
65
+ if block_given?
66
+ yield
67
+ else
68
+ span { plain(text) }
69
+ end
70
+
71
+ render(Icon.new(icon_right)) if icon_right
72
+ end
73
+
74
+ def base_class
75
+ [
76
+ "inline-flex items-center cursor-pointer shrink-0 justify-center rounded-md border gap-2 font-medium select-none",
77
+ # Disabled
78
+ "disabled:opacity-70 disabled:pointer-events-none",
79
+ # Focus
80
+ "focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-offset-2 focus-visible:ring-ring ring-offset-background",
81
+ # Icon
82
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0"
83
+ ]
84
+ end
85
+
86
+ def variant_class
87
+ case variant
88
+ when :default
89
+ [
90
+ "bg-background text-foreground border-border",
91
+ "hover:bg-zinc-50 dark:hover:bg-zinc-900"
92
+ ]
93
+ when :primary
94
+ [
95
+ "bg-primary text-white dark:text-zinc-950 border-primary",
96
+ "hover:bg-primary/90 dark:hover:bg-primary/90"
97
+ ]
98
+ when :destructive
99
+ [
100
+ "bg-destructive text-white border-destructive",
101
+ "hover:bg-destructive/90 dark:hover:bg-destructive/90",
102
+ "disabled:text-white/80"
103
+ ]
104
+ when :ghost
105
+ [
106
+ "bg-transparent text-foreground border-transparent",
107
+ "hover:bg-zinc-200/50 dark:hover:bg-zinc-900",
108
+ "disabled:text-muted-foreground"
109
+ ]
110
+ else
111
+ raise ArgumentError, "Unknown variant `#{variant}'"
112
+ end
113
+ end
114
+
115
+ def size_class
116
+ case size
117
+ when :xs
118
+ "px-1.5 h-6 text-xs [&_svg]:size-3"
119
+ when :sm
120
+ [
121
+ "px-2.5 h-7 text-sm [&_svg]:size-3",
122
+ "[&_svg:first-child:last-child]:-mx-1"
123
+ ]
124
+ when :md
125
+ [
126
+ "px-4 h-10 text-base [&_svg]:size-4",
127
+ # If icon only, make square
128
+ "[&_svg:first-child:last-child]:-mx-2"
129
+ ]
130
+ when :lg
131
+ [
132
+ "px-5 h-11 text-lg [&_svg]:size-5",
133
+ # If icon only, make square
134
+ "[&_svg:first-child:last-child]:-mx-2"
135
+ ]
136
+ else
137
+ raise ArgumentError, "Unknown size `#{size}'"
138
+ end
106
139
  end
107
140
  end
108
141
  end
@@ -1,19 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NitroKit
2
4
  class ButtonGroup < Component
3
- def view_template(&block)
4
- div(
5
- class: merge(
6
- [
7
- "flex -space-x-px isolate",
8
- # Remove rounded corners from middle buttons
9
- "[&>*:not(:first-child):not(:last-child)]:rounded-none [&>*:first-child:not(:last-child)]:rounded-r-none [&>*:last-child:not(:first-child)]:rounded-l-none",
10
- # Put focused button on top
11
- "[&>*]:focus:z-10",
12
- attrs[:class]
13
- ]
14
- ),
15
- &block
5
+ def initialize(**attrs)
6
+ super(
7
+ attrs,
8
+ class: [
9
+ "flex -space-x-px isolate",
10
+ # Remove rounded corners from middle buttons
11
+ "[&>*:not(:first-child):not(:last-child)]:rounded-none [&>*:first-child:not(:last-child)]:rounded-r-none [&>*:last-child:not(:first-child)]:rounded-l-none",
12
+ # Put focused button on top
13
+ "[&>*]:focus:z-10"
14
+ ]
16
15
  )
17
16
  end
17
+
18
+ def view_template
19
+ div(**attrs) do
20
+ yield
21
+ end
22
+ end
18
23
  end
19
24
  end
@@ -1,23 +1,63 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NitroKit
2
4
  class Card < Component
3
5
  def initialize(**attrs)
4
- @attrs = attrs
6
+ super(
7
+ attrs,
8
+ class: base_class
9
+ )
10
+ end
11
+
12
+ def view_template
13
+ div(**attrs) do
14
+ yield
15
+ end
16
+ end
17
+
18
+ def title(text = nil, **attrs, &block)
19
+ h2(**mattr(attrs, class: "text-lg font-bold -mb-2")) do
20
+ text_or_block(text, &block)
21
+ end
22
+ end
23
+
24
+ def body(text = nil, **attrs, &block)
25
+ div(**mattr(attrs, class: "text-muted-foreground text-sm leading-relaxed")) do
26
+ text_or_block(text, &block)
27
+ end
5
28
  end
6
29
 
7
- def view_template(&block)
8
- div(class: "rounded-lg border p-6 space-y-6 shadow-sm", &block)
30
+ def footer(text = nil, **attrs, &block)
31
+ div(**mattr(attrs, class: "flex gap-2 items-center")) do
32
+ text_or_block(text, &block)
33
+ end
9
34
  end
10
35
 
11
- def title(**attrs)
12
- h2(**attrs, class: merge(["text-lg font-bold", attrs[:class]])) { yield }
36
+ def divider(**attrs)
37
+ full_width do
38
+ hr(**attrs)
39
+ end
13
40
  end
14
41
 
15
- def body(**attrs)
16
- div(**attrs) { yield }
42
+ def full_width(**attrs)
43
+ div(**mattr(attrs, data: {slot: "full"}, class: "-mx-(--gap)")) do
44
+ yield
45
+ end
17
46
  end
18
47
 
19
- def footer(**attrs)
20
- div(**attrs, class: merge(["flex gap-2", attrs[:class]])) { yield }
48
+ private
49
+
50
+ def base_class
51
+ [
52
+ # Configure spacing with breakpoints
53
+ "[--gap:calc(var(--spacing)*4)] sm:[--gap:calc(var(--spacing)*6)]",
54
+ # Base styles
55
+ "flex flex-col items-stretch rounded-lg bg-background border p-(--gap) gap-(--gap) overflow-hidden",
56
+ # If a `data-slot=full` is the first thing, move it to the top
57
+ "[&>[data-slot=full]:first-child]:-mt-(--gap)",
58
+ # Group for hover, focus
59
+ "group/card"
60
+ ]
21
61
  end
22
62
  end
23
63
  end