nitro_kit 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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