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,23 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NitroKit
|
2
4
|
class Card < Component
|
3
5
|
def initialize(**attrs)
|
4
|
-
|
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
|
8
|
-
div(class: "
|
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
|
12
|
-
|
36
|
+
def divider(**attrs)
|
37
|
+
full_width do
|
38
|
+
hr(**attrs)
|
39
|
+
end
|
13
40
|
end
|
14
41
|
|
15
|
-
def
|
16
|
-
div(**attrs
|
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
|
-
|
20
|
-
|
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
|
@@ -1,37 +1,33 @@
|
|
1
1
|
module NitroKit
|
2
2
|
class Checkbox < Component
|
3
|
-
|
3
|
+
def initialize(label: nil, id: nil, **attrs)
|
4
|
+
@id = id || "nk--" + SecureRandom.hex(4)
|
5
|
+
@label = label
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
super(
|
8
|
+
attrs,
|
9
|
+
id: @id,
|
10
|
+
type: "checkbox",
|
11
|
+
class: input_class
|
12
|
+
)
|
10
13
|
end
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
:label_text
|
16
|
-
)
|
15
|
+
alias :html_label :label
|
16
|
+
|
17
|
+
attr_reader :label, :id
|
17
18
|
|
18
19
|
def view_template
|
19
|
-
div(class:
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
class: class_names(
|
25
|
-
"peer appearance-none shadow size-4 rounded border text-foreground",
|
26
|
-
"checked:bg-primary checked:border-primary",
|
27
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
28
|
-
)
|
29
|
-
)
|
20
|
+
div(class: wrapper_class) do
|
21
|
+
html_label(
|
22
|
+
class: "inline-grid *:[grid-area:1/1] shrink-0 place-items-center group/checkbox"
|
23
|
+
) do
|
24
|
+
input(**attrs)
|
30
25
|
checkmark
|
26
|
+
dash
|
31
27
|
end
|
32
28
|
|
33
|
-
if
|
34
|
-
render(Label.new(for:
|
29
|
+
if label.present? || block_given?
|
30
|
+
render(Label.new(for: id)) { label || yield }
|
35
31
|
end
|
36
32
|
end
|
37
33
|
end
|
@@ -39,24 +35,46 @@ module NitroKit
|
|
39
35
|
private
|
40
36
|
|
41
37
|
def checkmark
|
42
|
-
|
43
|
-
class:
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
38
|
+
svg(
|
39
|
+
class: merge_class(svg_class, "group-has-[:checked]/checkbox:visible"),
|
40
|
+
viewbox: "0 0 16 16",
|
41
|
+
fill: "none",
|
42
|
+
stroke: "currentColor",
|
43
|
+
stroke_linecap: "round",
|
44
|
+
stroke_linejoin: "round",
|
45
|
+
stroke_width: 3
|
46
|
+
) do |svg|
|
47
|
+
svg.path(d: "M 3 8 L 6 12 L 12 5")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def dash
|
52
|
+
svg(
|
53
|
+
class: merge_class(svg_class, "group-has-[:indeterminate]/checkbox:visible"),
|
54
|
+
viewbox: "0 0 16 16",
|
55
|
+
fill: "none",
|
56
|
+
stroke: "currentColor",
|
57
|
+
stroke_linecap: "round",
|
58
|
+
stroke_width: 3
|
59
|
+
) do |svg|
|
60
|
+
svg.line(x1: "3", y1: "8", x2: "12", y2: "8")
|
59
61
|
end
|
60
62
|
end
|
63
|
+
|
64
|
+
def input_class
|
65
|
+
[
|
66
|
+
"appearance-none shadow-sm size-4 rounded-sm border text-foreground",
|
67
|
+
"checked:bg-primary checked:border-primary indeterminate:bg-primary indeterminate:border-primary",
|
68
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ring-offset-2 ring-offset-background"
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
def svg_class
|
73
|
+
"size-3 text-zinc-50 [&>svg]:size-full dark:text-zinc-950 pointer-events-none invisible"
|
74
|
+
end
|
75
|
+
|
76
|
+
def wrapper_class
|
77
|
+
"isolate inline-flex items-center gap-2"
|
78
|
+
end
|
61
79
|
end
|
62
80
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class CheckboxGroup < Component
|
5
|
+
def initialize(options = nil, **attrs)
|
6
|
+
@options = options
|
7
|
+
|
8
|
+
super(
|
9
|
+
attrs,
|
10
|
+
class: "flex items-start flex-col gap-2"
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :options
|
15
|
+
|
16
|
+
def view_template
|
17
|
+
div(**attrs) do
|
18
|
+
if block_given?
|
19
|
+
yield
|
20
|
+
else
|
21
|
+
options.map { |option| item(*option) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def title(text = nil, **attrs, &block)
|
27
|
+
render(Label.new(**attrs)) do
|
28
|
+
text_or_block(text, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def item(text = nil, **attrs, &block)
|
33
|
+
render(Checkbox.new(**attrs)) do
|
34
|
+
text_or_block(text, &block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class Combobox < Component
|
5
|
+
def initialize(
|
6
|
+
options: [],
|
7
|
+
id: nil,
|
8
|
+
|
9
|
+
placement: "bottom",
|
10
|
+
tab_inserts_suggestions: true,
|
11
|
+
first_option_selection_mode: "selected",
|
12
|
+
scroll_into_view_options: {block: "nearest", inline: "nearest"},
|
13
|
+
|
14
|
+
**attrs
|
15
|
+
)
|
16
|
+
# floating-ui options
|
17
|
+
@placement = placement
|
18
|
+
|
19
|
+
# combobox-nav options
|
20
|
+
@tab_inserts_suggestions = tab_inserts_suggestions
|
21
|
+
@first_option_selection_mode = first_option_selection_mode
|
22
|
+
@scroll_into_view_options = scroll_into_view_options
|
23
|
+
|
24
|
+
@id = id || "nk--combobox-" + SecureRandom.hex(4)
|
25
|
+
|
26
|
+
@options = options
|
27
|
+
|
28
|
+
super(
|
29
|
+
attrs,
|
30
|
+
type: "text",
|
31
|
+
class: input_class,
|
32
|
+
data: {
|
33
|
+
nk__combobox_target: "input",
|
34
|
+
action: %w[
|
35
|
+
focusin->nk--combobox#open
|
36
|
+
focusin@window->nk--combobox#focusShift
|
37
|
+
click@window->nk--combobox#windowClick
|
38
|
+
input->nk--combobox#input
|
39
|
+
keydown.esc->nk--combobox#clear
|
40
|
+
keydown.down->nk--combobox#open
|
41
|
+
]
|
42
|
+
},
|
43
|
+
aria: {
|
44
|
+
controls: id(:listbox)
|
45
|
+
}
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader(
|
50
|
+
:options,
|
51
|
+
:placement,
|
52
|
+
:tab_inserts_suggestions,
|
53
|
+
:first_option_selection_mode,
|
54
|
+
:scroll_into_view_options
|
55
|
+
)
|
56
|
+
|
57
|
+
def view_template
|
58
|
+
div(
|
59
|
+
data: {
|
60
|
+
class: "isolate",
|
61
|
+
slot: "control",
|
62
|
+
controller: "nk--combobox",
|
63
|
+
nk__combobox_placement_value: placement,
|
64
|
+
nk__combobox_tab_inserts_suggestions_value: tab_inserts_suggestions.to_s,
|
65
|
+
nk__combobox_first_option_selection_mode_value: first_option_selection_mode.to_s,
|
66
|
+
nk__combobox_scroll_into_view_options_value: scroll_into_view_options&.to_json
|
67
|
+
}
|
68
|
+
) do
|
69
|
+
span(class: wrapper_class) do
|
70
|
+
render(Input.new(**attrs))
|
71
|
+
chevron_icon
|
72
|
+
end
|
73
|
+
|
74
|
+
# Since a combobox can function like a <select> element where the displayed
|
75
|
+
# value and the form value differ, include the value in a hidden field
|
76
|
+
input(
|
77
|
+
type: "hidden",
|
78
|
+
value: attrs[:value],
|
79
|
+
data: {nk__combobox_target: "hiddenField"}
|
80
|
+
)
|
81
|
+
|
82
|
+
ul(
|
83
|
+
role: "listbox",
|
84
|
+
id: id(:listbox),
|
85
|
+
class: list_class,
|
86
|
+
data: {nk__combobox_target: "list", state: "closed"}
|
87
|
+
) do
|
88
|
+
options.each do |(key, value)|
|
89
|
+
li(
|
90
|
+
role: "option",
|
91
|
+
data: {value:},
|
92
|
+
class: merge_class(option_class)
|
93
|
+
) { key }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def id(suffix)
|
102
|
+
"#{@id}-#{suffix}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def wrapper_class
|
106
|
+
"inline-grid *:[grid-area:1/1] group/combobox"
|
107
|
+
end
|
108
|
+
|
109
|
+
def input_class
|
110
|
+
"pr-8"
|
111
|
+
end
|
112
|
+
|
113
|
+
def list_class
|
114
|
+
[
|
115
|
+
"absolute top-0 left-0 p-1 bg-background rounded-md border shadow-sm w-fit max-w-sm flex-col flex z-10",
|
116
|
+
"max-h-60 overflow-y-auto",
|
117
|
+
"data-[state=closed]:hidden [&:not(:has([role=option]))]:hidden",
|
118
|
+
"[&_[aria-selected]]:bg-muted"
|
119
|
+
]
|
120
|
+
end
|
121
|
+
|
122
|
+
def option_class
|
123
|
+
"hidden flex-none px-2 py-1 rounded font-medium truncate cursor-pointer hover:bg-muted [&[role=option]]:block"
|
124
|
+
end
|
125
|
+
|
126
|
+
def chevron_icon
|
127
|
+
svg(
|
128
|
+
class: "size-4 self-center place-self-end mr-2 pointer-events-none text-muted-foreground group-hover/combobox:text-foreground",
|
129
|
+
viewbox: "0 0 24 24",
|
130
|
+
fill: "none",
|
131
|
+
stroke: "currentColor",
|
132
|
+
stroke_width: 1
|
133
|
+
) do |svg|
|
134
|
+
svg.path(d: "m6 9 6 6 6-6")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -1,30 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NitroKit
|
2
4
|
class Component < Phlex::HTML
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(**attrs)
|
6
|
-
@class_list = attrs.delete(:class)
|
7
|
-
@attrs = attrs.symbolize_keys
|
5
|
+
def initialize(*hashes, **defaults)
|
6
|
+
@attrs = merge_attrs(*hashes, **defaults)
|
8
7
|
end
|
9
8
|
|
10
|
-
attr_reader :
|
9
|
+
attr_reader :attrs
|
11
10
|
|
12
|
-
|
13
|
-
|
11
|
+
private
|
12
|
+
|
13
|
+
# Merge attributes with some special cases for matching keys
|
14
|
+
def merge_attrs(*hashes, **defaults)
|
15
|
+
defaults.merge(*hashes) do |key, old_value, new_value|
|
16
|
+
case key
|
17
|
+
when :class
|
18
|
+
# Use TailwindMerge to merge class names
|
19
|
+
merge_class(old_value, new_value)
|
20
|
+
when :data
|
21
|
+
# Merge data hashes with some special cases for Stimulus
|
22
|
+
merge_data(old_value, new_value)
|
23
|
+
else
|
24
|
+
new_value
|
25
|
+
end
|
26
|
+
end
|
14
27
|
end
|
15
28
|
|
16
|
-
|
17
|
-
|
18
|
-
|
29
|
+
alias :mattr :merge_attrs
|
30
|
+
|
31
|
+
def merge_class(*args)
|
32
|
+
@@merger ||= TailwindMerge::Merger.new
|
33
|
+
@@merger.merge(args)
|
19
34
|
end
|
20
35
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
36
|
+
def merge_data(*hashes)
|
37
|
+
hashes.compact.reduce({}) do |acc, hash|
|
38
|
+
acc.deep_merge(hash) do |key, old_val, new_val|
|
39
|
+
# Concat Stimulus actions
|
40
|
+
case key
|
41
|
+
when :action, :controller
|
42
|
+
[new_val, old_val].compact.join(" ")
|
43
|
+
else
|
44
|
+
new_val
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
24
49
|
|
25
|
-
|
26
|
-
|
27
|
-
|
50
|
+
def text_or_block(text = nil, &block)
|
51
|
+
if text
|
52
|
+
plain(text)
|
53
|
+
elsif block_given?
|
54
|
+
yield
|
55
|
+
else
|
56
|
+
nil
|
28
57
|
end
|
29
58
|
end
|
30
59
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class Dialog < Component
|
5
|
+
def initialize(identifier: nil, **attrs)
|
6
|
+
@identifier = identifier || SecureRandom.hex(6)
|
7
|
+
|
8
|
+
super(
|
9
|
+
attrs,
|
10
|
+
data: {controller: "nk--dialog", action: "click->nk--dialog#clickOutside"}
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :identifier
|
15
|
+
|
16
|
+
def view_template
|
17
|
+
div(**attrs) do
|
18
|
+
yield
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def trigger(text = nil, **attrs, &block)
|
23
|
+
render(
|
24
|
+
NitroKit::Button.new(**mattr(attrs, data: {nk__dialog_target: "trigger", action: "click->nk--dialog#open"}))
|
25
|
+
) do
|
26
|
+
text_or_block(text, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
alias :html_dialog :dialog
|
31
|
+
|
32
|
+
def dialog(**attrs)
|
33
|
+
html_dialog(
|
34
|
+
**mattr(
|
35
|
+
attrs,
|
36
|
+
class: dialog_class,
|
37
|
+
data: {nk__dialog_target: "dialog"},
|
38
|
+
aria: {
|
39
|
+
labelledby: id(:title),
|
40
|
+
describedby: id(:description)
|
41
|
+
}
|
42
|
+
)
|
43
|
+
) do
|
44
|
+
yield
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def close_button(**attrs)
|
49
|
+
render(
|
50
|
+
Button.new(
|
51
|
+
**mattr(
|
52
|
+
attrs,
|
53
|
+
variant: :ghost,
|
54
|
+
size: :sm,
|
55
|
+
class: "absolute top-2 right-2",
|
56
|
+
data: {action: "nk--dialog#close"}
|
57
|
+
)
|
58
|
+
)
|
59
|
+
) do
|
60
|
+
render(Icon.new(:x))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def title(text = nil, **attrs, &block)
|
65
|
+
h2(**mattr(attrs, id: id(:title), class: "text-lg font-semibold mb-2")) do
|
66
|
+
text_or_block(text, &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def description(text = nil, **attrs, &block)
|
71
|
+
div(
|
72
|
+
**mattr(
|
73
|
+
attrs,
|
74
|
+
id: id(:description),
|
75
|
+
class: "text-muted-foreground mb-6 text-sm leading-relaxed"
|
76
|
+
)
|
77
|
+
) do
|
78
|
+
text_or_block(text, &block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def id(suffix = nil)
|
85
|
+
"nk-#{identifier}#{suffix ? "-#{suffix}" : ""}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def dialog_class
|
89
|
+
[
|
90
|
+
"border rounded-xl max-w-lg w-full bg-background text-foreground shadow-lg m-auto p-6",
|
91
|
+
"dark:backdrop:bg-black/50"
|
92
|
+
]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|