okonomi_ui_kit 0.1.8 → 0.1.9

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -3
  3. data/app/assets/builds/okonomi_ui_kit/application.tailwind.css +409 -187
  4. data/app/helpers/okonomi_ui_kit/application_helper.rb +8 -0
  5. data/app/helpers/okonomi_ui_kit/attribute_section_helper.rb +5 -5
  6. data/app/helpers/okonomi_ui_kit/component.rb +10 -6
  7. data/app/helpers/okonomi_ui_kit/components/alert.rb +1 -1
  8. data/app/helpers/okonomi_ui_kit/components/badge.rb +3 -3
  9. data/app/helpers/okonomi_ui_kit/components/breadcrumbs.rb +4 -4
  10. data/app/helpers/okonomi_ui_kit/components/button_base.rb +6 -6
  11. data/app/helpers/okonomi_ui_kit/components/button_tag.rb +3 -3
  12. data/app/helpers/okonomi_ui_kit/components/button_to.rb +3 -3
  13. data/app/helpers/okonomi_ui_kit/components/code.rb +41 -37
  14. data/app/helpers/okonomi_ui_kit/components/confirmation_modal.rb +130 -0
  15. data/app/helpers/okonomi_ui_kit/components/forms/check_box_with_label.rb +38 -0
  16. data/app/helpers/okonomi_ui_kit/components/forms/collection_select.rb +57 -0
  17. data/app/helpers/okonomi_ui_kit/components/forms/date_field.rb +9 -0
  18. data/app/helpers/okonomi_ui_kit/components/forms/datetime_local_field.rb +9 -0
  19. data/app/helpers/okonomi_ui_kit/components/forms/email_field.rb +9 -0
  20. data/app/helpers/okonomi_ui_kit/components/forms/field.rb +24 -0
  21. data/app/helpers/okonomi_ui_kit/components/forms/field_set.rb +17 -0
  22. data/app/helpers/okonomi_ui_kit/components/forms/input_base.rb +57 -0
  23. data/app/helpers/okonomi_ui_kit/components/forms/label.rb +27 -0
  24. data/app/helpers/okonomi_ui_kit/components/forms/multi_select.rb +18 -0
  25. data/app/helpers/okonomi_ui_kit/components/forms/number_field.rb +9 -0
  26. data/app/helpers/okonomi_ui_kit/components/forms/password_field.rb +9 -0
  27. data/app/helpers/okonomi_ui_kit/components/forms/search_field.rb +9 -0
  28. data/app/helpers/okonomi_ui_kit/components/forms/select.rb +57 -0
  29. data/app/helpers/okonomi_ui_kit/components/forms/show_if.rb +28 -0
  30. data/app/helpers/okonomi_ui_kit/components/forms/telephone_field.rb +9 -0
  31. data/app/helpers/okonomi_ui_kit/components/forms/text_area.rb +9 -0
  32. data/app/helpers/okonomi_ui_kit/components/forms/text_field.rb +9 -0
  33. data/app/helpers/okonomi_ui_kit/components/forms/time_field.rb +9 -0
  34. data/app/helpers/okonomi_ui_kit/components/forms/upload_field.rb +25 -0
  35. data/app/helpers/okonomi_ui_kit/components/forms/url_field.rb +9 -0
  36. data/app/helpers/okonomi_ui_kit/components/forms.rb +6 -0
  37. data/app/helpers/okonomi_ui_kit/components/icon.rb +6 -6
  38. data/app/helpers/okonomi_ui_kit/components/link_to.rb +7 -7
  39. data/app/helpers/okonomi_ui_kit/components/navigation.rb +98 -0
  40. data/app/helpers/okonomi_ui_kit/components/page.rb +7 -7
  41. data/app/helpers/okonomi_ui_kit/components/table.rb +7 -8
  42. data/app/helpers/okonomi_ui_kit/components/typography.rb +16 -16
  43. data/app/helpers/okonomi_ui_kit/components.rb +4 -0
  44. data/app/helpers/okonomi_ui_kit/configs.rb +4 -0
  45. data/app/helpers/okonomi_ui_kit/form_builder.rb +39 -130
  46. data/app/helpers/okonomi_ui_kit/form_component.rb +7 -0
  47. data/app/helpers/okonomi_ui_kit/svg_icons.rb +5 -5
  48. data/app/helpers/okonomi_ui_kit/t_w_merge.rb +33 -27
  49. data/app/helpers/okonomi_ui_kit/ui_helper.rb +17 -58
  50. data/app/views/okonomi/components/confirmation_modal/_confirmation_modal.html.erb +76 -0
  51. data/app/views/okonomi/components/forms/check_box_with_label/_check_box_with_label.html.erb +6 -0
  52. data/app/views/okonomi/components/forms/field/_field.html.erb +3 -0
  53. data/app/views/okonomi/components/forms/field_set/_field_set.html.erb +3 -0
  54. data/app/views/okonomi/components/forms/upload_field/_upload_field.html.erb +1 -0
  55. data/app/views/okonomi/components/navigation/_link.html.erb +18 -0
  56. data/app/views/okonomi/components/navigation/_navigation.html.erb +4 -0
  57. data/app/views/okonomi/forms/tailwind/_checkbox_label.html.erb +2 -2
  58. data/app/views/okonomi/forms/tailwind/_field.html.erb +6 -6
  59. data/app/views/okonomi/forms/tailwind/_multi_select.html.erb +2 -4
  60. data/app/views/okonomi/forms/tailwind/_upload_field.html.erb +10 -10
  61. data/config/importmap.rb +1 -1
  62. data/lib/okonomi_ui_kit/engine.rb +0 -1
  63. data/lib/okonomi_ui_kit/version.rb +1 -1
  64. metadata +36 -10
  65. data/app/helpers/okonomi_ui_kit/navigation_helper.rb +0 -72
  66. data/app/helpers/okonomi_ui_kit/theme.rb +0 -136
  67. data/app/helpers/okonomi_ui_kit/theme_helper.rb +0 -17
  68. data/app/views/okonomi/forms/tailwind/_field_set.html.erb +0 -3
  69. data/app/views/okonomi/modals/_confirmation_modal.html.erb +0 -77
  70. data/app/views/okonomi/navigation/_link.html.erb +0 -15
  71. data/app/views/okonomi/navigation/_menu.html.erb +0 -3
  72. data/app/views/okonomi/navigation/_navbar.html.erb +0 -105
@@ -1,4 +1,12 @@
1
1
  module OkonomiUiKit
2
2
  module ApplicationHelper
3
+ def active_link_to(path, options = {}, &block)
4
+ path_to_check = path.split("?").first
5
+ if (options[:exact] && request.path == path_to_check) || (!options[:exact] && request.path.include?(path_to_check))
6
+ options[:class] = options[:active_class].presence || [ options[:class], "active" ].compact.join(" ")
7
+ end
8
+
9
+ link_to(path, options, &block)
10
+ end
3
11
  end
4
12
  end
@@ -3,7 +3,7 @@ module OkonomiUiKit
3
3
  def attribute_section(title:, description: nil, **options, &block)
4
4
  builder = AttributeSectionBuilder.new(self)
5
5
 
6
- render 'okonomi/attribute_sections/section',
6
+ render "okonomi/attribute_sections/section",
7
7
  builder: builder,
8
8
  title: title,
9
9
  description: description,
@@ -22,11 +22,11 @@ module OkonomiUiKit
22
22
  def attribute(label, value = nil, **options, &block)
23
23
  content = if block_given?
24
24
  capture(&block)
25
- elsif value.respond_to?(:call)
25
+ elsif value.respond_to?(:call)
26
26
  value.call
27
- else
27
+ else
28
28
  value
29
- end
29
+ end
30
30
 
31
31
  tag.div(class: "py-6 sm:grid sm:grid-cols-3 sm:gap-4") do
32
32
  dt_content = tag.dt(label, class: "text-sm font-medium text-gray-900")
@@ -47,4 +47,4 @@ module OkonomiUiKit
47
47
  end
48
48
  end
49
49
  end
50
- end
50
+ end
@@ -1,14 +1,13 @@
1
1
  module OkonomiUiKit
2
2
  class Component
3
- attr_reader :view, :theme
3
+ attr_reader :view
4
4
 
5
- def initialize(view, theme)
5
+ def initialize(view)
6
6
  @view = view
7
- @theme = theme || OkonomiUiKit::Theme::DEFAULT_THEME
8
7
  end
9
8
 
10
9
  def template_path
11
- "okonomi/components/#{name}/#{name}"
10
+ [ self.class.name.underscore.gsub("okonomi_ui_kit/", "okonomi/"), name ].join("/")
12
11
  end
13
12
 
14
13
  def name
@@ -50,7 +49,8 @@ module OkonomiUiKit
50
49
 
51
50
  raise ArgumentError, "Styles must be a Hash" unless styles.is_a?(Hash)
52
51
 
53
- internal_styles_registry[theme] = styles if styles.is_a?(Hash)
52
+ internal_styles_registry[theme] ||= {}
53
+ internal_styles_registry[theme] = deep_merge(internal_styles_registry[theme], styles)
54
54
  end
55
55
 
56
56
  def self.internal_styles_registry
@@ -84,7 +84,11 @@ module OkonomiUiKit
84
84
  end
85
85
 
86
86
  def self.config_class_name
87
- "OkonomiUiKit::Configs::#{name.demodulize}"
87
+ "#{config_namespace.name}::#{name.demodulize}"
88
+ end
89
+
90
+ def self.config_namespace
91
+ OkonomiUiKit::Configs
88
92
  end
89
93
 
90
94
  def self.config_class?
@@ -2,7 +2,7 @@ module OkonomiUiKit
2
2
  module Components
3
3
  class Alert < OkonomiUiKit::Component
4
4
  def render(title, options = {}, &block)
5
- view.render(template_path, title:, options: options.with_indifferent_access, &block)
5
+ view.render(template_path, title: title, options: options.with_indifferent_access, &block)
6
6
  end
7
7
  end
8
8
  end
@@ -7,9 +7,9 @@ module OkonomiUiKit
7
7
 
8
8
  classes = [
9
9
  style(:base),
10
- style(:severities, severity) || '',
11
- options.delete(:class) || ''
12
- ].reject(&:blank?).join(' ')
10
+ style(:severities, severity) || "",
11
+ options.delete(:class) || ""
12
+ ].reject(&:blank?).join(" ")
13
13
 
14
14
  view.tag.span(text, class: classes, **options)
15
15
  end
@@ -28,7 +28,7 @@ module OkonomiUiKit
28
28
  }
29
29
  end
30
30
 
31
- def initialize(template, options = {})
31
+ def initialize(template)
32
32
  super
33
33
  @items = []
34
34
  @builder = BreadcrumbBuilder.new(self)
@@ -36,9 +36,9 @@ module OkonomiUiKit
36
36
 
37
37
  def render(options = {}, &block)
38
38
  return "" if block.nil?
39
-
39
+
40
40
  block.call(@builder)
41
- view.render("okonomi/components/breadcrumbs/breadcrumbs",
41
+ view.render("okonomi/components/breadcrumbs/breadcrumbs",
42
42
  component: self,
43
43
  items: @items,
44
44
  options: options
@@ -66,4 +66,4 @@ module OkonomiUiKit
66
66
  end
67
67
  end
68
68
  end
69
- end
69
+ end
@@ -1,13 +1,13 @@
1
1
  module OkonomiUiKit
2
2
  module Components
3
3
  class ButtonBase < OkonomiUiKit::Component
4
- def build_button_class(variant:, color:, classes: '')
4
+ def build_button_class(variant:, color:, classes: "")
5
5
  [
6
- style(:root) || '',
7
- style(variant.to_sym, :root) || '',
8
- style(variant.to_sym, :colors, color.to_sym) || '',
9
- classes,
10
- ].reject(&:blank?).join(' ')
6
+ style(:root) || "",
7
+ style(variant.to_sym, :root) || "",
8
+ style(variant.to_sym, :colors, color.to_sym) || "",
9
+ classes
10
+ ].reject(&:blank?).join(" ")
11
11
  end
12
12
 
13
13
  register_styles :default do
@@ -7,8 +7,8 @@ module OkonomiUiKit
7
7
  options ||= {}
8
8
  options = options.with_indifferent_access
9
9
 
10
- variant = (options.delete(:variant) || 'contained').to_sym
11
- color = (options.delete(:color) || 'default').to_sym
10
+ variant = (options.delete(:variant) || "contained").to_sym
11
+ color = (options.delete(:color) || "default").to_sym
12
12
 
13
13
  options[:class] = build_button_class(variant: variant, color: color, classes: options[:class])
14
14
 
@@ -20,4 +20,4 @@ module OkonomiUiKit
20
20
  end
21
21
  end
22
22
  end
23
- end
23
+ end
@@ -7,8 +7,8 @@ module OkonomiUiKit
7
7
  html_options ||= {}
8
8
  html_options = html_options.with_indifferent_access
9
9
 
10
- variant = (html_options.delete(:variant) || 'contained').to_sym
11
- color = (html_options.delete(:color) || 'default').to_sym
10
+ variant = (html_options.delete(:variant) || "contained").to_sym
11
+ color = (html_options.delete(:color) || "default").to_sym
12
12
 
13
13
  html_options[:class] = build_button_class(variant: variant, color: color, classes: html_options[:class])
14
14
 
@@ -20,4 +20,4 @@ module OkonomiUiKit
20
20
  end
21
21
  end
22
22
  end
23
- end
23
+ end
@@ -8,61 +8,65 @@ module OkonomiUiKit
8
8
 
9
9
  # Extract component-specific options
10
10
  language = options.delete(:language) || options.delete(:lang)
11
- variant = (options.delete(:variant) || 'default').to_sym
12
- size = (options.delete(:size) || 'default').to_sym
11
+ variant = (options.delete(:variant) || "default").to_sym
12
+ size = (options.delete(:size) || "default").to_sym
13
13
  wrap = options.delete(:wrap) != false # Default to true
14
14
 
15
15
  # Build classes
16
16
  classes = build_classes(variant: variant, size: size, wrap: wrap, custom_class: options.delete(:class))
17
17
 
18
18
  # Escape HTML entities in content
19
- escaped_content = if block_given?
20
- view.capture(&block)
21
- elsif content
22
- content
23
- else
24
- ""
25
- end
19
+ raw_content = if block_given?
20
+ view.capture(&block)
21
+ elsif content
22
+ content
23
+ else
24
+ ""
25
+ end
26
+
27
+ escaped_content = html_escape(raw_content)
26
28
 
27
29
  view.render(
28
30
  template_path,
29
- content: escaped_content.strip.html_safe,
31
+ content: escaped_content,
30
32
  options: options,
31
33
  classes: classes,
32
34
  language: language
33
35
  )
34
36
  end
35
37
 
38
+ register_styles :default do
39
+ {
40
+ base: "",
41
+ variants: {
42
+ default: "bg-gray-900 text-gray-100 p-4 rounded-lg",
43
+ inline: "bg-gray-100 text-gray-900 px-1 py-0.5 rounded text-sm font-mono",
44
+ minimal: "bg-gray-900 text-gray-100 p-3 rounded text-xs"
45
+ },
46
+ sizes: {
47
+ xs: "text-xs",
48
+ sm: "text-sm",
49
+ default: "text-sm",
50
+ lg: "text-base"
51
+ },
52
+ wrap: {
53
+ true: "overflow-x-auto",
54
+ false: "overflow-hidden"
55
+ }
56
+ }
57
+ end
58
+
36
59
  private
37
60
 
38
61
  def build_classes(variant:, size:, wrap:, custom_class: nil)
39
- base_classes = theme.dig(:components, :code, :base) || "bg-gray-900 text-gray-100 rounded-lg"
40
-
41
- variant_classes = case variant
42
- when :inline
43
- "bg-gray-100 text-gray-900 px-1 py-0.5 rounded text-sm font-mono"
44
- when :minimal
45
- "bg-gray-900 text-gray-100 p-3 rounded text-xs"
46
- else
47
- # :default
48
- "bg-gray-900 text-gray-100 p-4 rounded-lg"
49
- end
50
-
51
- size_classes = case size
52
- when :xs
53
- "text-xs"
54
- when :sm
55
- "text-sm"
56
- when :lg
57
- "text-base"
58
- else
59
- # :default
60
- "text-sm"
61
- end
62
-
63
- wrap_classes = wrap ? "overflow-x-auto" : "overflow-hidden"
62
+ base_classes = style(:base) || ""
63
+ variant_classes = style(:variants, variant) || ""
64
+ size_classes = style(:sizes, size) || ""
65
+ # Convert boolean wrap to symbol for hash access
66
+ wrap_key = wrap ? :true : :false
67
+ wrap_classes = style(:wrap, wrap_key) || ""
64
68
 
65
- [base_classes, variant_classes, size_classes, wrap_classes, custom_class].compact.join(' ')
69
+ [ base_classes, variant_classes, size_classes, wrap_classes, custom_class ].reject(&:blank?).join(" ")
66
70
  end
67
71
 
68
72
  def html_escape(content)
@@ -70,4 +74,4 @@ module OkonomiUiKit
70
74
  end
71
75
  end
72
76
  end
73
- end
77
+ end
@@ -0,0 +1,130 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ class ConfirmationModal < OkonomiUiKit::Component
4
+ def render(options = {}, &block)
5
+ options = options.with_indifferent_access
6
+
7
+ # Extract and validate required options
8
+ title = options.fetch(:title) { raise ArgumentError, "title is required" }
9
+ message = options.fetch(:message) { raise ArgumentError, "message is required" }
10
+
11
+ # Extract optional parameters with defaults
12
+ confirm_text = options.delete(:confirm_text) || "Confirm"
13
+ cancel_text = options.delete(:cancel_text) || "Cancel"
14
+ variant = (options.delete(:variant) || :warning).to_sym
15
+ size = (options.delete(:size) || :md).to_sym
16
+ auto_open = options.delete(:auto_open) || false
17
+
18
+ # Build component options
19
+ modal_options = {
20
+ title: title,
21
+ message: message,
22
+ confirm_text: confirm_text,
23
+ cancel_text: cancel_text,
24
+ variant: variant,
25
+ size: size,
26
+ auto_open: auto_open,
27
+ has_custom_actions: block_given?,
28
+ data: options.delete(:data) || {}
29
+ }.merge(options)
30
+
31
+ view.render(template_path, component: self, options: modal_options, &block)
32
+ end
33
+
34
+ # Register default styles for the confirmation modal
35
+ register_styles :default do
36
+ {
37
+ # Modal container and backdrop
38
+ backdrop: "fixed inset-0 bg-gray-500/75 transition-opacity duration-300 ease-out opacity-0",
39
+ container: "fixed inset-0 z-10 w-screen overflow-y-auto",
40
+ wrapper: "flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0",
41
+
42
+ # Modal panel
43
+ panel: {
44
+ base: "relative transform overflow-hidden rounded-lg bg-white px-4 pt-5 pb-4 text-left shadow-xl transition-all duration-300 ease-out sm:my-8 sm:w-full sm:p-6 opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
45
+ sizes: {
46
+ sm: "sm:max-w-sm",
47
+ md: "sm:max-w-lg",
48
+ lg: "sm:max-w-2xl",
49
+ xl: "sm:max-w-4xl"
50
+ }
51
+ },
52
+
53
+ # Close button
54
+ close_button: {
55
+ wrapper: "absolute top-0 right-0 hidden pt-4 pr-4 sm:block",
56
+ button: "rounded-md bg-white text-gray-400 hover:text-gray-500 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none",
57
+ icon: {
58
+ file: "heroicons/outline/x-mark",
59
+ class: "size-6"
60
+ }
61
+ },
62
+
63
+ # Icon configuration
64
+ icon: {
65
+ wrapper: "mx-auto flex size-12 shrink-0 items-center justify-center rounded-full sm:mx-0 sm:size-10",
66
+ class: "size-6",
67
+ variants: {
68
+ warning: {
69
+ wrapper: "bg-red-100",
70
+ icon: "text-red-600",
71
+ file: "heroicons/outline/exclamation-triangle"
72
+ },
73
+ info: {
74
+ wrapper: "bg-blue-100",
75
+ icon: "text-blue-600",
76
+ file: "heroicons/outline/information-circle"
77
+ },
78
+ success: {
79
+ wrapper: "bg-green-100",
80
+ icon: "text-green-600",
81
+ file: "heroicons/outline/check-circle"
82
+ }
83
+ }
84
+ },
85
+
86
+ # Content styling
87
+ content: {
88
+ wrapper: "sm:flex sm:items-start",
89
+ text_wrapper: "mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",
90
+ title: "text-base font-semibold text-gray-900",
91
+ message: "mt-2 text-sm text-gray-500"
92
+ },
93
+
94
+ # Actions container
95
+ actions: {
96
+ wrapper: "mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"
97
+ }
98
+ }
99
+ end
100
+
101
+ # Helper methods to build classes from styles
102
+ def modal_panel_class(size)
103
+ [
104
+ style(:panel, :base),
105
+ style(:panel, :sizes, size)
106
+ ].compact.join(" ")
107
+ end
108
+
109
+ def modal_icon_wrapper_class(variant)
110
+ [
111
+ style(:icon, :wrapper),
112
+ style(:icon, :variants, variant, :wrapper)
113
+ ].compact.join(" ")
114
+ end
115
+
116
+ def modal_icon_class(variant)
117
+ [
118
+ style(:icon, :class),
119
+ style(:icon, :variants, variant, :icon)
120
+ ].compact.join(" ")
121
+ end
122
+
123
+ def modal_data_attributes(options)
124
+ return "" unless options[:data]
125
+
126
+ options[:data].map { |k, v| "data-#{k.to_s.dasherize}=\"#{v}\"" }.join(" ").html_safe
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,38 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class CheckBoxWithLabel < OkonomiUiKit::FormComponent
5
+ def render(form, method, options = {}, checked_value = true, unchecked_value = false)
6
+ options = options.with_indifferent_access
7
+
8
+ view.content_tag(:div, class: style(:wrapper)) do
9
+ view.concat form.check_box(
10
+ method,
11
+ {
12
+ class: style(:input, :root)
13
+ }.merge(options || {}),
14
+ checked_value,
15
+ unchecked_value
16
+ )
17
+ view.concat view.render(template_path, component: self, method: method, options: options, form: form)
18
+ end
19
+ end
20
+
21
+ register_styles :default do
22
+ {
23
+ wrapper: "flex gap-x-3",
24
+ input: {
25
+ root: "size-4 rounded border-gray-300 text-primary-600 focus:ring-primary-600"
26
+ },
27
+ label: {
28
+ root: "text-sm/6 font-medium text-gray-900"
29
+ },
30
+ hint: {
31
+ root: "text-gray-500"
32
+ }
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class CollectionSelect < OkonomiUiKit::FormComponent
5
+ def render(form, method, collection, value_method, text_method, options = {}, html_options = {})
6
+ html_options = html_options.with_indifferent_access
7
+ css = build_select_classes(form, method, html_options)
8
+
9
+ select_html = form.collection_select(method, collection, value_method, text_method, options, html_options.merge(class: css))
10
+ icon_html = view.ui.icon(
11
+ style(:icon, :file),
12
+ class: style(:icon, :class)
13
+ )
14
+
15
+ view.content_tag(:div, class: style(:wrapper)) do
16
+ view.concat(select_html)
17
+ view.concat(icon_html)
18
+ end
19
+ end
20
+
21
+ register_styles :default do
22
+ {
23
+ wrapper: "relative",
24
+ root: "w-full appearance-none border-0 pl-3 pr-10 py-2 rounded-md ring-1 focus:outline-none focus-within:ring-1",
25
+ error: "bg-danger-100 text-danger-400 ring-danger-400 focus:ring-danger-600",
26
+ valid: "text-default-700 ring-gray-300 focus-within:ring-gray-400",
27
+ icon: {
28
+ file: "heroicons/mini/chevron-down",
29
+ class: "absolute right-2 top-1/2 -translate-y-1/2 size-5 text-gray-400 pointer-events-none"
30
+ }
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ def build_select_classes(form, method, html_options)
37
+ css = [
38
+ style(:root),
39
+ when_errors(form, method, style(:error), style(:valid)),
40
+ html_options[:class]
41
+ ].compact.join(" ").split(" ").uniq
42
+
43
+ TWMerge.merge(css)
44
+ end
45
+
46
+ def when_errors(form, method, value, default_value = nil)
47
+ key = method.to_s.gsub("_id", "").to_sym
48
+ if form.object.errors.include?(key) || form.object.errors.include?(method)
49
+ value
50
+ else
51
+ default_value
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,9 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class DateField < OkonomiUiKit::Components::Forms::InputBase
5
+ type :date
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class DatetimeLocalField < OkonomiUiKit::Components::Forms::InputBase
5
+ type :datetime_local
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class EmailField < OkonomiUiKit::Components::Forms::InputBase
5
+ type :email
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class Field < OkonomiUiKit::FormComponent
5
+ def render(form, field_id = nil, options = {}, &block)
6
+ view.render(template_path, component: self, form: form, field_id: field_id, options: options, &block)
7
+ end
8
+
9
+ register_styles :default do
10
+ {
11
+ wrapper: "w-full flex flex-col gap-2",
12
+ header: "flex justify-between items-center",
13
+ hint: {
14
+ trigger: "text-primary-600 text-sm hover:cursor-help",
15
+ content: "text-xs absolute border rounded-md bg-gray-100 border-gray-600 text-gray-600 p-1 z-10"
16
+ },
17
+ content: "block",
18
+ error: "mt-1 text-danger-600 text-sm"
19
+ }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class FieldSet < OkonomiUiKit::FormComponent
5
+ def render(form, options = {}, &block)
6
+ view.render(template_path, component: self, options: options, form: form, &block)
7
+ end
8
+
9
+ register_styles :default do
10
+ {
11
+ root: "w-full flex flex-col gap-4 col-span-1 sm:col-span-3 md:col-span-5"
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ module OkonomiUiKit
2
+ module Components
3
+ module Forms
4
+ class InputBase < OkonomiUiKit::FormComponent
5
+ def render_arguments(object, method, options = {})
6
+ css = input_field_classes(object, method, self.class.type_value, options)
7
+ [ method, { autocomplete: "off" }.merge(options).merge(class: css) ]
8
+ end
9
+
10
+ register_styles :default do
11
+ {
12
+ root: "w-full border-0 px-3 py-2 rounded-md ring-1 focus:outline-none focus-within:ring-1",
13
+ error: "bg-danger-100 text-danger-400 ring-danger-400 focus:ring-danger-600",
14
+ valid: "text-default-700 ring-gray-300 focus-within:ring-gray-400",
15
+ disabled: "disabled:bg-gray-50 disabled:cursor-not-allowed"
16
+ }
17
+ end
18
+
19
+ def self.type(type)
20
+ @type = type
21
+ end
22
+
23
+ def self.type_value
24
+ @type || :text
25
+ end
26
+
27
+ def input_field_classes(object, method, type, options, include_disabled: true)
28
+ css_classes = OkonomiUiKit::TWMerge.merge_all(
29
+ style(:root),
30
+ when_errors(
31
+ object,
32
+ method,
33
+ style(:error),
34
+ style(:valid)
35
+ ),
36
+ options[:class]
37
+ )
38
+
39
+ if include_disabled
40
+ css_classes = OkonomiUiKit::TWMerge.merge_all(css_classes, style(:disabled))
41
+ end
42
+
43
+ css_classes
44
+ end
45
+
46
+ def when_errors(object, method, value, default_value = nil)
47
+ key = method.to_s.gsub("_id", "").to_sym
48
+ if object.errors.include?(key) || object.errors.include?(method)
49
+ value
50
+ else
51
+ default_value
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end