nitro_kit 0.1.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 +7 -0
- data/README.md +1 -0
- data/Rakefile +6 -0
- data/app/components/nitro_kit/accordion.rb +60 -0
- data/app/components/nitro_kit/badge.rb +34 -0
- data/app/components/nitro_kit/button.rb +109 -0
- data/app/components/nitro_kit/button_group.rb +19 -0
- data/app/components/nitro_kit/card.rb +23 -0
- data/app/components/nitro_kit/checkbox.rb +62 -0
- data/app/components/nitro_kit/component.rb +31 -0
- data/app/components/nitro_kit/dropdown.rb +110 -0
- data/app/components/nitro_kit/field.rb +37 -0
- data/app/components/nitro_kit/field_group.rb +14 -0
- data/app/components/nitro_kit/fieldset.rb +16 -0
- data/app/components/nitro_kit/form_builder.rb +43 -0
- data/app/components/nitro_kit/icon.rb +23 -0
- data/app/components/nitro_kit/label.rb +10 -0
- data/app/components/nitro_kit/radio_button.rb +63 -0
- data/app/components/nitro_kit/radio_group.rb +35 -0
- data/app/components/nitro_kit/switch.rb +66 -0
- data/app/helpers/application_helper.rb +89 -0
- data/app/helpers/nitro_kit/accordion_helper.rb +7 -0
- data/app/helpers/nitro_kit/badge_helper.rb +15 -0
- data/app/helpers/nitro_kit/button_group_helper.rb +7 -0
- data/app/helpers/nitro_kit/button_helper.rb +40 -0
- data/app/helpers/nitro_kit/card_helper.rb +7 -0
- data/app/helpers/nitro_kit/checkbox_helper.rb +24 -0
- data/app/helpers/nitro_kit/dropdown_helper.rb +7 -0
- data/app/helpers/nitro_kit/field_helper.rb +7 -0
- data/app/helpers/nitro_kit/icon_helper.rb +7 -0
- data/app/helpers/nitro_kit/label_helper.rb +15 -0
- data/app/helpers/nitro_kit/radio_button_helper.rb +20 -0
- data/app/helpers/nitro_kit/switch_helper.rb +15 -0
- data/lib/generators/nitro_kit/add_generator.rb +87 -0
- data/lib/generators/nitro_kit/install_generator.rb +9 -0
- data/lib/nitro_kit/railtie.rb +8 -0
- data/lib/nitro_kit/schema_builder.rb +47 -0
- data/lib/nitro_kit/variants.rb +21 -0
- data/lib/nitro_kit/version.rb +3 -0
- data/lib/nitro_kit.rb +8 -0
- metadata +127 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
module NitroKit
|
2
|
+
class RadioGroup < Component
|
3
|
+
include ActionView::Helpers::FormTagHelper
|
4
|
+
|
5
|
+
def initialize(name, value: nil, **options)
|
6
|
+
super(**options)
|
7
|
+
@name = name
|
8
|
+
@group_value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :name, :id, :group_value
|
12
|
+
|
13
|
+
def view_template
|
14
|
+
div(class: "flex items-start flex-col gap-2") do
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def title(text = nil, **options)
|
20
|
+
render(Label.new(**options)) { text || yield }
|
21
|
+
end
|
22
|
+
|
23
|
+
def item(value, text = nil, **options)
|
24
|
+
render(
|
25
|
+
RadioButton.new(
|
26
|
+
name,
|
27
|
+
id:,
|
28
|
+
value:,
|
29
|
+
checked: group_value.presence == value,
|
30
|
+
**options
|
31
|
+
)
|
32
|
+
) { text || yield }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module NitroKit
|
2
|
+
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
|
+
def initialize(
|
28
|
+
name,
|
29
|
+
checked: false,
|
30
|
+
disabled: false,
|
31
|
+
size: :base,
|
32
|
+
description: nil,
|
33
|
+
**attrs
|
34
|
+
)
|
35
|
+
super(**attrs)
|
36
|
+
|
37
|
+
@name = name
|
38
|
+
@checked = checked
|
39
|
+
@disabled = disabled
|
40
|
+
@size = size
|
41
|
+
@description = description
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_reader :name, :checked, :disabled, :description, :size
|
45
|
+
|
46
|
+
def view_template
|
47
|
+
button(
|
48
|
+
**attrs,
|
49
|
+
type: "button",
|
50
|
+
class: merge([BUTTON, SIZE[size], attrs[:class]]),
|
51
|
+
data: data_merge(attrs[:data], {controller: "nk--switch", action: "nk--switch#switch"}),
|
52
|
+
role: "switch",
|
53
|
+
aria: {checked: checked.to_s}
|
54
|
+
) do
|
55
|
+
span(class: "sr-only") { description }
|
56
|
+
handle
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def handle
|
63
|
+
span(aria: {hidden: true}, class: HANDLE, data: {slot: "handle"})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module ApplicationHelper
|
2
|
+
def component_page(&block)
|
3
|
+
tag.div(class: "p-5") do
|
4
|
+
capture(&block)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def title(text)
|
9
|
+
tag.h1(class: "text-3xl font-semibold mb-6") { text }
|
10
|
+
end
|
11
|
+
|
12
|
+
def lead(text)
|
13
|
+
tag.p(class: "text-lg text-gray-600 dark:text-gray-400 mb-6") { text }
|
14
|
+
end
|
15
|
+
|
16
|
+
def section(**attrs)
|
17
|
+
tag.div(**attrs, class: class_names("mt-8 space-y-4", attrs[:class])) { yield }
|
18
|
+
end
|
19
|
+
|
20
|
+
def section_title(text)
|
21
|
+
tag.h2(class: "text-2xl font-semibold mb-4") { text }
|
22
|
+
end
|
23
|
+
|
24
|
+
def example(**attrs)
|
25
|
+
tag
|
26
|
+
.div(
|
27
|
+
**attrs,
|
28
|
+
class: class_names(
|
29
|
+
["flex gap-2 justify-center items-center min-h-[200px] py-12 p-5 w-full rounded border", attrs[:class]]
|
30
|
+
)
|
31
|
+
) do
|
32
|
+
yield
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def code_example(str)
|
37
|
+
formatter = Rouge::Formatters::HTML.new
|
38
|
+
lexer = Rouge::Lexers::ERB.new
|
39
|
+
|
40
|
+
tag
|
41
|
+
.div(
|
42
|
+
class: "bg-[#f7f8fa] dark:bg-[#161b22] divide-y border rounded overflow-hidden",
|
43
|
+
data: {controller: "copy-to-clipboard"}
|
44
|
+
) do
|
45
|
+
concat(
|
46
|
+
tag.div(class: "px-1 py-1 flex w-full") do
|
47
|
+
concat(
|
48
|
+
nk_ghost_button(
|
49
|
+
icon: :copy,
|
50
|
+
size: :xs,
|
51
|
+
data: {copy_to_clipboard_target: "button", action: "copy-to-clipboard#copy"}
|
52
|
+
) { "Copy" }
|
53
|
+
)
|
54
|
+
concat(
|
55
|
+
nk_ghost_button(
|
56
|
+
icon: :check,
|
57
|
+
size: :xs,
|
58
|
+
data: {copy_to_clipboard_target: "successMessage"},
|
59
|
+
class: "hidden"
|
60
|
+
) { "Copied!" }
|
61
|
+
)
|
62
|
+
end
|
63
|
+
)
|
64
|
+
|
65
|
+
concat(
|
66
|
+
tag
|
67
|
+
.pre(
|
68
|
+
class: "text-sm highlight py-3 px-4 font-mono overflow-scroll",
|
69
|
+
data: {copy_to_clipboard_target: "source"}
|
70
|
+
) do
|
71
|
+
tag.code do
|
72
|
+
formatter.format(lexer.lex(str.strip_heredoc.strip)).html_safe
|
73
|
+
end
|
74
|
+
end
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def markdown(str)
|
80
|
+
Commonmarker
|
81
|
+
.to_html(
|
82
|
+
str.strip_heredoc,
|
83
|
+
options: {
|
84
|
+
parse: {smart: true}
|
85
|
+
}
|
86
|
+
)
|
87
|
+
.html_safe
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module BadgeHelper
|
3
|
+
include Variants
|
4
|
+
|
5
|
+
automatic_variants(Badge::VARIANTS, :nk_badge)
|
6
|
+
|
7
|
+
def nk_badge(text = nil, **attrs, &block)
|
8
|
+
content = text || capture(&block)
|
9
|
+
|
10
|
+
render(NitroKit::Badge.new(**attrs)) do
|
11
|
+
content
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module ButtonHelper
|
3
|
+
include Variants
|
4
|
+
|
5
|
+
automatic_variants(Button::VARIANTS, :nk_button)
|
6
|
+
|
7
|
+
def nk_button(
|
8
|
+
text_or_href = nil,
|
9
|
+
href = nil,
|
10
|
+
icon: nil,
|
11
|
+
icon_right: nil,
|
12
|
+
size: :base,
|
13
|
+
type: :button,
|
14
|
+
variant: :default,
|
15
|
+
**attrs,
|
16
|
+
&block
|
17
|
+
)
|
18
|
+
content = block_given? ? capture(&block) : text_or_href
|
19
|
+
href = text_or_href if href.nil? && block_given?
|
20
|
+
|
21
|
+
if href && !href.is_a?(String)
|
22
|
+
href = url_for(href)
|
23
|
+
end
|
24
|
+
|
25
|
+
render(
|
26
|
+
NitroKit::Button.new(
|
27
|
+
href:,
|
28
|
+
icon:,
|
29
|
+
icon_right:,
|
30
|
+
size:,
|
31
|
+
type:,
|
32
|
+
variant:,
|
33
|
+
**attrs
|
34
|
+
)
|
35
|
+
) do
|
36
|
+
content
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module CheckboxHelper
|
3
|
+
# Match the signature of the Rails `check_box` helper
|
4
|
+
def nk_checkbox(name, *args)
|
5
|
+
if args.length >= 4
|
6
|
+
raise ArgumentError, "wrong number of arguments (given #{args.length + 1}, expected 1..4)"
|
7
|
+
end
|
8
|
+
|
9
|
+
options = args.extract_options!
|
10
|
+
value, checked = args.empty? ? ["1", false] : [*args, false]
|
11
|
+
|
12
|
+
attrs = {
|
13
|
+
:type => "checkbox",
|
14
|
+
:name => name,
|
15
|
+
:id => sanitize_to_id(name),
|
16
|
+
:value => value
|
17
|
+
}.update(options.symbolize_keys)
|
18
|
+
|
19
|
+
attrs[:checked] = "checked" if checked
|
20
|
+
|
21
|
+
render(NitroKit::Checkbox.new(name, value:, **attrs))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module LabelHelper
|
3
|
+
def nk_label(name = nil, content_or_options = nil, options = nil, &block)
|
4
|
+
if block_given? && content_or_options.is_a?(Hash)
|
5
|
+
options = content_or_options = content_or_options.symbolize_keys
|
6
|
+
else
|
7
|
+
options ||= {}
|
8
|
+
options = options.symbolize_keys
|
9
|
+
end
|
10
|
+
|
11
|
+
options[:for] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
|
12
|
+
render(Label.new(**options)) { content_or_options || name.to_s.humanize || yield }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module RadioButtonHelper
|
3
|
+
def nk_radio_button(name, value, *args)
|
4
|
+
if args.length >= 3
|
5
|
+
raise ArgumentError, "wrong number of arguments (given #{args.length + 2}, expected 2..4)"
|
6
|
+
end
|
7
|
+
|
8
|
+
options = args.extract_options!
|
9
|
+
checked = args.empty? ? false : args.first
|
10
|
+
|
11
|
+
options[:checked] = "checked" if checked
|
12
|
+
|
13
|
+
render(RadioButton.new(name, value:, name:, value:, **options))
|
14
|
+
end
|
15
|
+
|
16
|
+
def nk_radio_group(name, **options, &block)
|
17
|
+
render(RadioGroup.new(name, **options, &block))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module SwitchHelper
|
3
|
+
def nk_switch(
|
4
|
+
name,
|
5
|
+
checked: false,
|
6
|
+
disabled: false,
|
7
|
+
size: :base,
|
8
|
+
description: nil,
|
9
|
+
**attrs,
|
10
|
+
&block
|
11
|
+
)
|
12
|
+
render(NitroKit::Switch.new(name, checked:, disabled:, size:, description:, **attrs), &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "nitro_kit/schema_builder"
|
2
|
+
|
3
|
+
module NitroKit
|
4
|
+
class AddGenerator < Rails::Generators::Base
|
5
|
+
argument :component_names, type: :array
|
6
|
+
|
7
|
+
source_root File.expand_path("../../../", __dir__)
|
8
|
+
|
9
|
+
extend SchemaBuilder
|
10
|
+
|
11
|
+
SCHEMA = build_schema do |s|
|
12
|
+
s.add(:badge)
|
13
|
+
s.add(
|
14
|
+
:button,
|
15
|
+
components: [:button, :button_group],
|
16
|
+
helpers: [:button, :button_group]
|
17
|
+
)
|
18
|
+
s.add(
|
19
|
+
:dropdown,
|
20
|
+
js: [:dropdown],
|
21
|
+
modules: ["@floating-ui/core", "@floating-ui/dom"]
|
22
|
+
)
|
23
|
+
s.add(
|
24
|
+
:icon,
|
25
|
+
gems: ["lucide-rails"]
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy_base_component
|
30
|
+
copy_file("app/components/nitro_kit/component.rb", "app/components/nitro_kit/component.rb")
|
31
|
+
end
|
32
|
+
|
33
|
+
def copy_component_files
|
34
|
+
components.each do |component|
|
35
|
+
component.files.each do |path|
|
36
|
+
copy_file(path, path)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_gems
|
42
|
+
gems = components.flat_map(&:gems)
|
43
|
+
|
44
|
+
return unless gems.any?
|
45
|
+
|
46
|
+
gems.each do |name|
|
47
|
+
gem(name)
|
48
|
+
end
|
49
|
+
|
50
|
+
run("bundle install")
|
51
|
+
end
|
52
|
+
|
53
|
+
def install_modules
|
54
|
+
modules = components.flat_map(&:modules)
|
55
|
+
|
56
|
+
return unless modules.any?
|
57
|
+
|
58
|
+
if importmaps?
|
59
|
+
run("bin/importmap pin #{modules.join(" ")}")
|
60
|
+
else
|
61
|
+
say("oh hai npm/yarn/bun")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def components
|
68
|
+
list = component_names
|
69
|
+
|
70
|
+
if list == ["all"]
|
71
|
+
list = SCHEMA.all
|
72
|
+
end
|
73
|
+
|
74
|
+
list.map do |name|
|
75
|
+
unless component = SCHEMA[name.to_sym]
|
76
|
+
raise "Unknown component `#{name}'"
|
77
|
+
end
|
78
|
+
|
79
|
+
component
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def importmaps?
|
84
|
+
File.exist?(File.expand_path("bin/importmap", Rails.root))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module SchemaBuilder
|
3
|
+
Component = Struct.new(:files, :modules, :gems)
|
4
|
+
|
5
|
+
class Builder
|
6
|
+
def initialize
|
7
|
+
@schema = {}
|
8
|
+
yield self
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(
|
12
|
+
name,
|
13
|
+
components: nil,
|
14
|
+
helpers: nil,
|
15
|
+
js: [],
|
16
|
+
modules: [],
|
17
|
+
gems: []
|
18
|
+
)
|
19
|
+
# Default is one component, one helper with same name
|
20
|
+
components ||= [name]
|
21
|
+
helpers ||= [name]
|
22
|
+
|
23
|
+
@schema[name] = Component.new(
|
24
|
+
[
|
25
|
+
components.map { |c| "app/components/nitro_kit/#{c}.rb" },
|
26
|
+
helpers.map { |c| "app/helpers/nitro_kit/#{c}_helper.rb" },
|
27
|
+
js.map { |c| "app/javascript/controllers/nk/#{c}_controller.js" }
|
28
|
+
].flatten,
|
29
|
+
modules,
|
30
|
+
gems
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](key)
|
35
|
+
@schema[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
def all
|
39
|
+
@schema.keys.map(&:to_s)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_schema(&block)
|
44
|
+
Builder.new(&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module NitroKit
|
2
|
+
module Variants
|
3
|
+
module ClassMethods
|
4
|
+
def automatic_variants(variants, method_name)
|
5
|
+
_, prefix, original = method_name.match(/(nk_)(.+)/).to_a
|
6
|
+
|
7
|
+
variants.each do |variant, class_name|
|
8
|
+
variant_method_name = "#{prefix}#{variant}_#{original}"
|
9
|
+
|
10
|
+
define_method(variant_method_name) do |*args, **kwargs, &block|
|
11
|
+
send(method_name, *args, variant:, **kwargs, &block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
base.extend(ClassMethods)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|