nitro_kit 0.1.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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +20 -0
- data/README.md +28 -1
- data/Rakefile +6 -4
- data/app/components/nitro_kit/accordion.rb +69 -33
- data/app/components/nitro_kit/alert.rb +69 -0
- data/app/components/nitro_kit/avatar.rb +52 -0
- data/app/components/nitro_kit/badge.rb +47 -23
- data/app/components/nitro_kit/button.rb +97 -65
- data/app/components/nitro_kit/button_group.rb +18 -13
- data/app/components/nitro_kit/card.rb +49 -9
- data/app/components/nitro_kit/checkbox.rb +59 -41
- data/app/components/nitro_kit/checkbox_group.rb +38 -0
- data/app/components/nitro_kit/combobox.rb +138 -0
- data/app/components/nitro_kit/component.rb +46 -17
- data/app/components/nitro_kit/datepicker.rb +9 -0
- data/app/components/nitro_kit/dialog.rb +95 -0
- data/app/components/nitro_kit/dropdown.rb +116 -73
- data/app/components/nitro_kit/field.rb +281 -30
- data/app/components/nitro_kit/field_group.rb +10 -5
- data/app/components/nitro_kit/fieldset.rb +42 -7
- data/app/components/nitro_kit/form_builder.rb +45 -22
- data/app/components/nitro_kit/icon.rb +29 -8
- data/app/components/nitro_kit/input.rb +26 -0
- data/app/components/nitro_kit/label.rb +18 -5
- data/app/components/nitro_kit/pagination.rb +98 -0
- data/app/components/nitro_kit/radio_button.rb +28 -27
- data/app/components/nitro_kit/radio_button_group.rb +53 -0
- data/app/components/nitro_kit/select.rb +72 -0
- data/app/components/nitro_kit/switch.rb +49 -39
- data/app/components/nitro_kit/table.rb +56 -0
- data/app/components/nitro_kit/tabs.rb +98 -0
- data/app/components/nitro_kit/textarea.rb +26 -0
- data/app/components/nitro_kit/toast.rb +104 -0
- data/app/components/nitro_kit/tooltip.rb +53 -0
- data/app/helpers/nitro_kit/accordion_helper.rb +3 -1
- data/app/helpers/nitro_kit/alert_helper.rb +11 -0
- data/app/helpers/nitro_kit/avatar_helper.rb +9 -0
- data/app/helpers/nitro_kit/badge_helper.rb +3 -5
- data/app/helpers/nitro_kit/button_group_helper.rb +2 -0
- data/app/helpers/nitro_kit/button_helper.rb +37 -28
- data/app/helpers/nitro_kit/card_helper.rb +2 -0
- data/app/helpers/nitro_kit/checkbox_helper.rb +19 -16
- data/app/helpers/nitro_kit/combobox_helper.rb +9 -0
- data/app/helpers/nitro_kit/datepicker_helper.rb +9 -0
- data/app/helpers/nitro_kit/dialog_helper.rb +9 -0
- data/app/helpers/nitro_kit/dropdown_helper.rb +3 -1
- data/app/helpers/nitro_kit/field_group_helper.rb +9 -0
- data/app/helpers/nitro_kit/field_helper.rb +4 -2
- data/app/helpers/nitro_kit/fieldset_helper.rb +9 -0
- data/app/helpers/nitro_kit/form_helper.rb +13 -0
- data/app/helpers/nitro_kit/icon_helper.rb +3 -1
- data/app/helpers/nitro_kit/input_helper.rb +35 -0
- data/app/helpers/nitro_kit/label_helper.rb +12 -8
- data/app/helpers/nitro_kit/pagination_helper.rb +42 -0
- data/app/helpers/nitro_kit/radio_button_helper.rb +15 -12
- data/app/helpers/nitro_kit/select_helper.rb +24 -0
- data/app/helpers/nitro_kit/switch_helper.rb +4 -10
- data/app/helpers/nitro_kit/table_helper.rb +9 -0
- data/app/helpers/nitro_kit/tabs_helper.rb +9 -0
- data/app/helpers/nitro_kit/textarea_helper.rb +9 -0
- data/app/helpers/nitro_kit/toast_helper.rb +36 -0
- data/app/helpers/nitro_kit/tooltip_helper.rb +9 -0
- data/lib/generators/nitro_kit/add_generator.rb +38 -41
- data/lib/generators/nitro_kit/install_generator.rb +2 -1
- data/lib/nitro_kit/engine.rb +4 -0
- data/lib/nitro_kit/schema_builder.rb +90 -16
- data/lib/nitro_kit/version.rb +1 -1
- data/lib/nitro_kit.rb +39 -1
- data/lib/tasks/nitro_kit_tasks.rake +4 -0
- metadata +40 -12
- data/app/components/nitro_kit/radio_group.rb +0 -35
- data/app/helpers/application_helper.rb +0 -89
- data/lib/nitro_kit/railtie.rb +0 -8
@@ -1,43 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NitroKit
|
2
4
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
3
5
|
# Fields
|
4
6
|
|
5
|
-
def fieldset(
|
6
|
-
|
7
|
-
@template.render(NitroKit::Fieldset.new(options)) { content }
|
7
|
+
def fieldset(**attrs, &block)
|
8
|
+
@template.render(NitroKit::Fieldset.new(**attrs), &block)
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
-
|
12
|
-
@template.render(NitroKit::FieldGroup.new(**options)) { content }
|
13
|
-
end
|
11
|
+
def field(field_name, **attrs, &block)
|
12
|
+
label = attrs.fetch(:label, field_name.to_s.humanize)
|
14
13
|
|
15
|
-
|
16
|
-
label = options.fetch(:label, object_name.to_s.humanize)
|
17
|
-
errors = object.errors.include?(object_name) ? object.errors.full_messages_for(object_name) : nil
|
14
|
+
errors = object && object.errors.include?(field_name) ? object.errors.full_messages_for(field_name) : nil
|
18
15
|
|
19
|
-
@template.render(NitroKit::Field.new(
|
16
|
+
@template.render(NitroKit::Field.new(self, field_name, label:, errors:, **attrs), &block)
|
20
17
|
end
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
def label(object_name, method, content_or_options = nil, options = nil, &block)
|
19
|
+
def group(**attrs, &block)
|
20
|
+
@template.render(FieldGroup.new(**attrs), &block)
|
25
21
|
end
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
# Inputs
|
24
|
+
|
25
|
+
%i[
|
26
|
+
checkbox
|
27
|
+
color_field
|
28
|
+
date_field
|
29
|
+
datetime_field
|
30
|
+
datetime_local_field
|
31
|
+
email_field
|
32
|
+
file_field
|
33
|
+
hidden_field
|
34
|
+
month_field
|
35
|
+
number_field
|
36
|
+
password_field
|
37
|
+
phone_field
|
38
|
+
radio_button
|
39
|
+
range_field
|
40
|
+
search_field
|
41
|
+
telephone_field
|
42
|
+
text_area
|
43
|
+
text_field
|
44
|
+
time_field
|
45
|
+
url_field
|
46
|
+
week_field
|
47
|
+
]
|
48
|
+
.each do |method|
|
49
|
+
define_method(method) do |*args, **attrs, &block|
|
50
|
+
@template.send("nk_#{method}", *args, **attrs, &block)
|
51
|
+
end
|
52
|
+
end
|
30
53
|
|
31
|
-
|
54
|
+
# Buttons
|
32
55
|
|
33
|
-
def submit(value = "Save changes", **
|
56
|
+
def submit(value = "Save changes", **attrs)
|
34
57
|
content = value || @template.capture(&block)
|
35
|
-
@template.render(NitroKit::Button.new(variant: :primary, type: :submit, **
|
58
|
+
@template.render(NitroKit::Button.new(variant: :primary, type: :submit, **attrs)) { content }
|
36
59
|
end
|
37
60
|
|
38
|
-
def button(value = "Save changes", **
|
61
|
+
def button(value = "Save changes", **attrs)
|
39
62
|
content = value || @template.capture(&block)
|
40
|
-
@template.render(NitroKit::Button.new(**
|
63
|
+
@template.render(NitroKit::Button.new(**attrs)) { content }
|
41
64
|
end
|
42
65
|
end
|
43
66
|
end
|
@@ -1,23 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NitroKit
|
2
4
|
class Icon < Component
|
3
5
|
include Phlex::Rails::Helpers::ContentTag
|
4
6
|
include LucideRails::RailsHelper
|
5
7
|
|
6
|
-
|
7
|
-
sm: "size-4",
|
8
|
-
base: "size-5",
|
9
|
-
}
|
10
|
-
|
11
|
-
def initialize(name:, size: :base, **attrs)
|
8
|
+
def initialize(name, size: :md, **attrs)
|
12
9
|
@name = name
|
13
10
|
@size = size
|
14
|
-
|
11
|
+
|
12
|
+
super(
|
13
|
+
attrs,
|
14
|
+
class: size_class,
|
15
|
+
stroke_width: 1.5
|
16
|
+
)
|
15
17
|
end
|
16
18
|
|
17
19
|
attr_reader :name, :size
|
18
20
|
|
19
21
|
def view_template
|
20
|
-
lucide_icon(name, **
|
22
|
+
lucide_icon(name, **dasherized_attrs)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def size_class
|
28
|
+
case size
|
29
|
+
when :sm
|
30
|
+
"size-4"
|
31
|
+
when :md
|
32
|
+
"size-5"
|
33
|
+
when :lg
|
34
|
+
"size-7"
|
35
|
+
else
|
36
|
+
raise ArgumentError, "Unknown size `#{size}'"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def dasherized_attrs
|
41
|
+
attrs.transform_keys { |k| k.to_s.dasherize }
|
21
42
|
end
|
22
43
|
end
|
23
44
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class Input < Component
|
5
|
+
def initialize(**attrs)
|
6
|
+
super(
|
7
|
+
attrs,
|
8
|
+
class: base_class
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def view_template
|
13
|
+
input(**attrs)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def base_class
|
19
|
+
[
|
20
|
+
"block rounded-md border bg-background border-border text-base px-3 py-2 h-10",
|
21
|
+
# Focus
|
22
|
+
"focus:outline-none ring-ring ring-offset-2 ring-offset-background focus-visible:ring-2"
|
23
|
+
]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,10 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NitroKit
|
2
4
|
class Label < Component
|
3
|
-
def
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
def initialize(text = nil, **attrs)
|
6
|
+
@text = text
|
7
|
+
|
8
|
+
super(
|
9
|
+
attrs,
|
10
|
+
class: "text-sm font-medium select-none",
|
11
|
+
data: {slot: "label"}
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :text
|
16
|
+
|
17
|
+
def view_template(&block)
|
18
|
+
label(**attrs) do
|
19
|
+
text_or_block(text, &block)
|
20
|
+
end
|
8
21
|
end
|
9
22
|
end
|
10
23
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class Pagination < Component
|
5
|
+
def initialize(**attrs)
|
6
|
+
super(
|
7
|
+
attrs,
|
8
|
+
class: merge_class(nav_class, attrs[:class]),
|
9
|
+
aria: {label: "Pagination"}
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def view_template
|
14
|
+
nav(**attrs) do
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def prev(text = nil, **attrs, &block)
|
20
|
+
page_link(**mattr(attrs, aria: {label: "Previous page"})) do
|
21
|
+
if text || block_given?
|
22
|
+
text_or_block(text, &block)
|
23
|
+
else
|
24
|
+
render(Icon.new("arrow-left"))
|
25
|
+
plain("Previous")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def next(text = nil, **attrs, &block)
|
31
|
+
page_link(**mattr(attrs, aria: {label: "Next page"})) do
|
32
|
+
if text || block_given?
|
33
|
+
text_or_block(text, &block)
|
34
|
+
else
|
35
|
+
plain("Next")
|
36
|
+
render(Icon.new("arrow-right"))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def page(text = nil, current: false, **attrs, &block)
|
42
|
+
page_link(
|
43
|
+
**mattr(
|
44
|
+
attrs,
|
45
|
+
aria: {
|
46
|
+
current: current ? "page" : nil
|
47
|
+
},
|
48
|
+
disabled: current,
|
49
|
+
class: [page_class, current && "bg-zinc-200/50 dark:bg-zinc-800/50"]
|
50
|
+
)
|
51
|
+
) do
|
52
|
+
text_or_block(text, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def ellipsis(**attrs)
|
57
|
+
render(
|
58
|
+
Button.new(
|
59
|
+
**mattr(
|
60
|
+
attrs,
|
61
|
+
variant: :ghost,
|
62
|
+
disabled: true,
|
63
|
+
class: page_class
|
64
|
+
)
|
65
|
+
)
|
66
|
+
) do
|
67
|
+
"…"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def page_link(text = nil, disabled: nil, **attrs)
|
74
|
+
a(
|
75
|
+
**mattr(
|
76
|
+
attrs,
|
77
|
+
role: "link",
|
78
|
+
aria: {disabled: disabled.to_s},
|
79
|
+
class: link_class
|
80
|
+
)
|
81
|
+
) do
|
82
|
+
yield
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def nav_class
|
87
|
+
"flex items-center justify-center gap-1 text-sm font-medium"
|
88
|
+
end
|
89
|
+
|
90
|
+
def link_class
|
91
|
+
"inline-flex items-center justify-center rounded-md border font-medium h-9 px-3 gap-2 border-transparent aria-disabled:text-muted-foreground [&>svg]:size-4"
|
92
|
+
end
|
93
|
+
|
94
|
+
def page_class
|
95
|
+
"w-9 px-0"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -1,44 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NitroKit
|
2
4
|
class RadioButton < Component
|
3
5
|
include ActionView::Helpers::FormTagHelper
|
4
6
|
|
5
|
-
def initialize(
|
6
|
-
|
7
|
+
def initialize(label: nil, **attrs)
|
8
|
+
@label = label
|
9
|
+
@id = id || SecureRandom.hex(4)
|
10
|
+
|
11
|
+
@class = attrs.delete(:class)
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
13
|
+
super(
|
14
|
+
attrs,
|
15
|
+
type: "radio",
|
16
|
+
id: @id,
|
17
|
+
class: [
|
18
|
+
"peer appearance-none size-5 shadow-sm rounded-full border text-foreground bg-background",
|
19
|
+
"[&[aria-checked='true']]:bg-primary",
|
20
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
21
|
+
]
|
22
|
+
)
|
12
23
|
end
|
13
24
|
|
25
|
+
alias :html_label :label
|
26
|
+
|
14
27
|
attr_reader(
|
15
|
-
:name,
|
16
|
-
:value,
|
17
28
|
:id,
|
18
|
-
:
|
29
|
+
:label
|
19
30
|
)
|
20
31
|
|
21
32
|
def view_template
|
22
|
-
div(class:
|
23
|
-
|
24
|
-
input(
|
25
|
-
**attrs,
|
26
|
-
type: "radio",
|
27
|
-
name:,
|
28
|
-
value:,
|
29
|
-
id:,
|
30
|
-
class: class_names(
|
31
|
-
"peer row-start-1 col-start-1",
|
32
|
-
"appearance-none size-5 shadow rounded-full border text-foreground bg-background",
|
33
|
-
"[&[aria-checked='true']]:bg-primary",
|
34
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
35
|
-
)
|
36
|
-
)
|
33
|
+
div(class: merge_class("inline-flex items-center gap-2", @class)) do
|
34
|
+
html_label(class: "inline-grid *:[grid-area:1/1] place-items-center") do
|
35
|
+
input(**attrs)
|
37
36
|
dot
|
38
37
|
end
|
39
38
|
|
40
|
-
if
|
41
|
-
render(Label.new(for: id))
|
39
|
+
if label.present? || block_given?
|
40
|
+
render(Label.new(for: id)) do
|
41
|
+
label || (block_given? ? yield : nil)
|
42
|
+
end
|
42
43
|
end
|
43
44
|
end
|
44
45
|
end
|
@@ -47,7 +48,7 @@ module NitroKit
|
|
47
48
|
|
48
49
|
def dot
|
49
50
|
svg(
|
50
|
-
class:
|
51
|
+
class: merge_class(
|
51
52
|
"row-start-1 col-start-1",
|
52
53
|
"size-2.5 text-primay opacity-0 pointer-events-none",
|
53
54
|
"peer-checked:opacity-100"
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class RadioButtonGroup < Component
|
5
|
+
def initialize(options = nil, name: nil, value: nil, **attrs)
|
6
|
+
@options = options
|
7
|
+
|
8
|
+
@name = name
|
9
|
+
@group_value = value
|
10
|
+
|
11
|
+
super(
|
12
|
+
attrs,
|
13
|
+
name: @name,
|
14
|
+
class: "flex items-start flex-col gap-2"
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :name, :group_value, :options
|
19
|
+
|
20
|
+
def view_template
|
21
|
+
div(**attrs) do
|
22
|
+
if block_given?
|
23
|
+
yield
|
24
|
+
else
|
25
|
+
options.map { |o| item(*o) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def title(text = nil, **attrs, &block)
|
31
|
+
render(Label.new(**attrs)) do
|
32
|
+
text_or_block(text, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def item(text = nil, value_as_arg = nil, value: nil, **attrs, &block)
|
37
|
+
value ||= value_as_arg
|
38
|
+
|
39
|
+
render(
|
40
|
+
RadioButton.new(
|
41
|
+
**mattr(
|
42
|
+
attrs,
|
43
|
+
name: attrs.fetch(:name, name),
|
44
|
+
value:,
|
45
|
+
checked: group_value.presence == value
|
46
|
+
)
|
47
|
+
)
|
48
|
+
) do
|
49
|
+
text_or_block(text, &block)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class Select < Component
|
5
|
+
def initialize(options = nil, value: nil, include_empty: false, prompt: nil, index: nil, **attrs)
|
6
|
+
@options = options
|
7
|
+
@value = value
|
8
|
+
@include_empty = include_empty
|
9
|
+
@prompt = prompt
|
10
|
+
@index = index
|
11
|
+
|
12
|
+
super(
|
13
|
+
attrs,
|
14
|
+
class: wrapper_class
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :value, :options, :include_empty, :prompts, :index
|
19
|
+
|
20
|
+
def view_template
|
21
|
+
span(class: wrapper_class, data: {slot: "control"}) do
|
22
|
+
select(**attrs, class: select_class) do
|
23
|
+
option if include_empty
|
24
|
+
options ? options.map { |o| option(*o) } : yield
|
25
|
+
end
|
26
|
+
|
27
|
+
chevron_icon
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :html_option :option
|
32
|
+
|
33
|
+
def option(key_or_value = nil, value = nil, **attrs, &block)
|
34
|
+
value ||= key_or_value
|
35
|
+
|
36
|
+
html_option(**attrs, selected: @value == value) do
|
37
|
+
if block_given?
|
38
|
+
yield
|
39
|
+
else
|
40
|
+
key_or_value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def wrapper_class
|
48
|
+
"w-fit inline-grid *:[grid-area:1/1] group/select"
|
49
|
+
end
|
50
|
+
|
51
|
+
def select_class
|
52
|
+
[
|
53
|
+
"appearance-none bg-background text-foreground rounded-md border px-3 py-2 pr-10 w-full",
|
54
|
+
# Focus
|
55
|
+
"focus:outline-none ring-ring ring-offset-2 ring-offset-background focus-visible:ring-2"
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
def chevron_icon
|
60
|
+
svg(
|
61
|
+
class: "size-4 self-center place-self-end mr-1.5 text-muted-foreground pointer-events-none group-hover/select:text-foreground",
|
62
|
+
viewbox: "0 0 24 24",
|
63
|
+
fill: "none",
|
64
|
+
stroke: "currentColor",
|
65
|
+
stroke_width: 1
|
66
|
+
) do |svg|
|
67
|
+
svg.path(d: "m7 15 5 5 5-5")
|
68
|
+
svg.path(d: "m7 9 5-5 5 5")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,56 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NitroKit
|
2
4
|
class Switch < Component
|
3
|
-
BUTTON = [
|
4
|
-
"inline-flex items-center shrink-0",
|
5
|
-
"bg-background rounded-full border",
|
6
|
-
"transition-colors duration-200 ease-in-out",
|
7
|
-
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ring-offset-background",
|
8
|
-
|
9
|
-
# Checked
|
10
|
-
"[&[aria-checked='true']]:bg-foreground [&[aria-checked='true']]:border-foreground",
|
11
|
-
|
12
|
-
# Checked > Handle
|
13
|
-
"[&[aria-checked='false']_[data-slot='handle']]:bg-primary",
|
14
|
-
"[&[aria-checked='true']_[data-slot='handle']]:translate-x-[calc(theme(spacing.5)-1px)] [&[aria-checked='true']_[data-slot='handle']]:bg-background"
|
15
|
-
].freeze
|
16
|
-
|
17
|
-
HANDLE = [
|
18
|
-
"pointer-events-none inline-block rounded-full shadow ring-0",
|
19
|
-
"transition translate-x-[3px] duration-200 ease-in-out"
|
20
|
-
].freeze
|
21
|
-
|
22
|
-
SIZE = {
|
23
|
-
base: "h-6 w-10 [&_[data-slot=handle]]:size-4 [&[aria-checked='true']_[data-slot='handle']]:translate-x-[calc(theme(spacing.5)-1px)]",
|
24
|
-
sm: "h-5 w-8 [&_[data-slot=handle]]:size-3 [&[aria-checked='true']_[data-slot='handle']]:translate-x-[calc(theme(spacing.4)-1px)]"
|
25
|
-
}
|
26
|
-
|
27
5
|
def initialize(
|
28
|
-
name,
|
29
6
|
checked: false,
|
30
|
-
|
31
|
-
size: :base,
|
7
|
+
size: :md,
|
32
8
|
description: nil,
|
33
9
|
**attrs
|
34
10
|
)
|
35
|
-
super(**attrs)
|
36
|
-
|
37
|
-
@name = name
|
38
11
|
@checked = checked
|
39
|
-
@disabled = disabled
|
40
12
|
@size = size
|
41
13
|
@description = description
|
14
|
+
|
15
|
+
super(**attrs)
|
42
16
|
end
|
43
17
|
|
44
|
-
attr_reader :
|
18
|
+
attr_reader :checked, :description, :size
|
45
19
|
|
46
20
|
def view_template
|
47
21
|
button(
|
48
|
-
**
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
22
|
+
**mattr(
|
23
|
+
**attrs,
|
24
|
+
type: "button",
|
25
|
+
class: [base_class, size_class],
|
26
|
+
data: {controller: "nk--switch", action: "nk--switch#toggle"},
|
27
|
+
role: "switch",
|
28
|
+
aria: {checked: checked.to_s}
|
29
|
+
)
|
54
30
|
) do
|
55
31
|
span(class: "sr-only") { description }
|
56
32
|
handle
|
@@ -60,7 +36,41 @@ module NitroKit
|
|
60
36
|
private
|
61
37
|
|
62
38
|
def handle
|
63
|
-
span(aria: {hidden: true}, class:
|
39
|
+
span(aria: {hidden: true}, class: handle_class, data: {slot: "handle"})
|
40
|
+
end
|
41
|
+
|
42
|
+
def base_class
|
43
|
+
[
|
44
|
+
"inline-flex items-center shrink-0",
|
45
|
+
"bg-background rounded-full border",
|
46
|
+
"transition-colors duration-200 ease-in-out",
|
47
|
+
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ring-offset-background",
|
48
|
+
|
49
|
+
# Checked
|
50
|
+
"[&[aria-checked=true]]:bg-foreground [&[aria-checked=true]]:border-foreground",
|
51
|
+
|
52
|
+
# Checked > Handle
|
53
|
+
"[&[aria-checked=false]_[data-slot=handle]]:bg-primary",
|
54
|
+
"[&[aria-checked=true]_[data-slot=handle]]:bg-background"
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def handle_class
|
59
|
+
[
|
60
|
+
"pointer-events-none inline-block rounded-full shadow-sm ring-0",
|
61
|
+
"transition translate-x-[3px] duration-200 ease-in-out"
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
def size_class
|
66
|
+
case size
|
67
|
+
when :md
|
68
|
+
"h-6 w-10 [&_[data-slot=handle]]:size-4 [&[aria-checked=true]_[data-slot=handle]]:translate-x-[calc(theme(spacing.5)-1px)]"
|
69
|
+
when :sm
|
70
|
+
"h-5 w-8 [&_[data-slot=handle]]:size-3 [&[aria-checked=true]_[data-slot=handle]]:translate-x-[calc(theme(spacing.4)-1px)]"
|
71
|
+
else
|
72
|
+
raise ArgumentError, "Unknown size: #{size}"
|
73
|
+
end
|
64
74
|
end
|
65
75
|
end
|
66
76
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class Table < Component
|
5
|
+
def initialize(**attrs)
|
6
|
+
super(
|
7
|
+
attrs,
|
8
|
+
class: "w-full caption-bottom text-sm divide-y"
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def view_template
|
13
|
+
div(class: "relative w-full overflow-auto") do
|
14
|
+
table(**attrs) do
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :html_thead :thead
|
21
|
+
alias :html_tbody :tbody
|
22
|
+
alias :html_tr :tr
|
23
|
+
alias :html_th :th
|
24
|
+
alias :html_td :td
|
25
|
+
|
26
|
+
def thead(**attrs)
|
27
|
+
html_thead(**attrs) { yield }
|
28
|
+
end
|
29
|
+
|
30
|
+
def tbody(**attrs)
|
31
|
+
html_tbody(**mattr(attrs, class: "[&_tr:last-child]:border-0")) { yield }
|
32
|
+
end
|
33
|
+
|
34
|
+
def tr(**attrs)
|
35
|
+
html_tr(**mattr(attrs, class: "border-b")) { yield }
|
36
|
+
end
|
37
|
+
|
38
|
+
def th(text = nil, **attrs, &block)
|
39
|
+
html_th(**mattr(attrs, class: [cell_classes, "font-medium text-left"])) do
|
40
|
+
text_or_block(text, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def td(text = nil, **attrs, &block)
|
45
|
+
html_td(**mattr(attrs, class: cell_classes)) do
|
46
|
+
text_or_block(text, &block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def cell_classes
|
53
|
+
"py-3 px-2"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|