uchi 0.1.5 → 0.1.6

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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/uchi/application.js +2655 -24
  3. data/app/assets/javascripts/uchi.js +11 -0
  4. data/app/assets/stylesheets/uchi/application.css +982 -1311
  5. data/app/assets/tailwind/uchi.css +4 -4
  6. data/app/components/uchi/field/belongs_to/edit.html.erb +1 -1
  7. data/app/components/uchi/field/belongs_to/index.html.erb +1 -1
  8. data/app/components/uchi/field/belongs_to/show.html.erb +3 -5
  9. data/app/components/uchi/field/belongs_to.rb +30 -7
  10. data/app/components/uchi/field/boolean/edit.html.erb +1 -1
  11. data/app/components/uchi/field/date/edit.html.erb +1 -1
  12. data/app/components/uchi/field/date_time/edit.html.erb +1 -1
  13. data/app/components/uchi/field/file/edit.html.erb +1 -1
  14. data/app/components/uchi/field/file/index.html.erb +2 -1
  15. data/app/components/uchi/field/file/show.html.erb +3 -2
  16. data/app/components/uchi/field/has_and_belongs_to_many/show.html.erb +5 -3
  17. data/app/components/uchi/field/has_many/show.html.erb +5 -3
  18. data/app/components/uchi/field/id/index.html.erb +1 -1
  19. data/app/components/uchi/field/id/show.html.erb +1 -1
  20. data/app/components/uchi/field/image/edit.html.erb +1 -1
  21. data/app/components/uchi/field/image/index.html.erb +2 -1
  22. data/app/components/uchi/field/image/show.html.erb +2 -1
  23. data/app/components/uchi/field/number/edit.html.erb +1 -1
  24. data/app/components/uchi/field/string/edit.html.erb +1 -1
  25. data/app/components/uchi/field/text/edit.html.erb +1 -1
  26. data/app/components/{flowbite → uchi/flowbite}/breadcrumb.rb +5 -5
  27. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_home.rb +2 -2
  28. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item/current.rb +3 -3
  29. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item/first.rb +4 -4
  30. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item.rb +4 -4
  31. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_separator.rb +7 -5
  32. data/app/components/uchi/flowbite/button/outline.rb +37 -0
  33. data/app/components/uchi/flowbite/button/pill.rb +40 -0
  34. data/app/components/uchi/flowbite/button.rb +93 -0
  35. data/app/components/uchi/flowbite/card/card.html.erb +7 -0
  36. data/app/components/uchi/flowbite/card/title.rb +37 -0
  37. data/app/components/uchi/flowbite/card.rb +84 -0
  38. data/app/components/{flowbite → uchi/flowbite}/input/checkbox.rb +7 -7
  39. data/app/components/{flowbite → uchi/flowbite}/input/date.rb +1 -1
  40. data/app/components/{flowbite → uchi/flowbite}/input/date_time.rb +1 -1
  41. data/app/components/{flowbite → uchi/flowbite}/input/email.rb +1 -1
  42. data/app/components/{flowbite → uchi/flowbite}/input/field.rb +11 -10
  43. data/app/components/uchi/flowbite/input/file.rb +30 -0
  44. data/app/components/{flowbite → uchi/flowbite}/input/hint.rb +6 -5
  45. data/app/components/{flowbite → uchi/flowbite}/input/label.rb +8 -7
  46. data/app/components/{flowbite → uchi/flowbite}/input/number.rb +1 -1
  47. data/app/components/{flowbite → uchi/flowbite}/input/password.rb +1 -1
  48. data/app/components/{flowbite → uchi/flowbite}/input/phone.rb +1 -1
  49. data/app/components/{flowbite → uchi/flowbite}/input/radio_button.rb +7 -7
  50. data/app/components/{flowbite → uchi/flowbite}/input/select.rb +16 -7
  51. data/app/components/uchi/flowbite/input/textarea.rb +42 -0
  52. data/app/components/{flowbite → uchi/flowbite}/input/url.rb +1 -1
  53. data/app/components/uchi/flowbite/input/validation_error.rb +39 -0
  54. data/app/components/uchi/flowbite/input_field/checkbox.html.erb +9 -0
  55. data/app/components/{flowbite → uchi/flowbite}/input_field/checkbox.rb +10 -6
  56. data/app/components/{flowbite → uchi/flowbite}/input_field/date.rb +2 -2
  57. data/app/components/{flowbite → uchi/flowbite}/input_field/date_time.rb +2 -2
  58. data/app/components/{flowbite → uchi/flowbite}/input_field/email.rb +2 -2
  59. data/app/components/{flowbite → uchi/flowbite}/input_field/file.rb +2 -2
  60. data/app/components/uchi/flowbite/input_field/input_field.html.erb +9 -0
  61. data/app/components/{flowbite → uchi/flowbite}/input_field/number.rb +2 -2
  62. data/app/components/{flowbite → uchi/flowbite}/input_field/password.rb +2 -2
  63. data/app/components/{flowbite → uchi/flowbite}/input_field/phone.rb +2 -2
  64. data/app/components/uchi/flowbite/input_field/radio_button.html.erb +9 -0
  65. data/app/components/{flowbite → uchi/flowbite}/input_field/radio_button.rb +13 -9
  66. data/app/components/{flowbite → uchi/flowbite}/input_field/select.rb +7 -3
  67. data/app/components/{flowbite → uchi/flowbite}/input_field/text.rb +1 -1
  68. data/app/components/{flowbite → uchi/flowbite}/input_field/textarea.rb +2 -2
  69. data/app/components/{flowbite → uchi/flowbite}/input_field/url.rb +2 -2
  70. data/app/components/{flowbite → uchi/flowbite}/input_field.rb +19 -5
  71. data/app/components/uchi/flowbite/link.rb +41 -0
  72. data/app/components/{flowbite → uchi/flowbite}/style.rb +1 -1
  73. data/app/components/{flowbite → uchi/flowbite}/toast/icon.rb +1 -1
  74. data/app/components/uchi/flowbite/toast/toast.html.erb +40 -0
  75. data/app/components/{flowbite → uchi/flowbite}/toast.rb +2 -2
  76. data/app/components/uchi/ui/actions/dropdown/dropdown.html.erb +59 -0
  77. data/app/components/uchi/ui/actions/dropdown.rb +32 -0
  78. data/app/components/uchi/ui/breadcrumb/breadcrumb.html.erb +4 -4
  79. data/app/components/uchi/ui/form/input/collection_checkboxes.html.erb +2 -1
  80. data/app/components/uchi/ui/form/input/collection_checkboxes.rb +12 -17
  81. data/app/components/uchi/ui/frame/frame.html.erb +6 -1
  82. data/app/components/uchi/ui/index/records_table/records_table.html.erb +109 -18
  83. data/app/components/uchi/ui/index/records_table/search_form/search_form.html.erb +21 -5
  84. data/app/components/uchi/ui/navigation/navigation.html.erb +9 -0
  85. data/app/components/uchi/ui/navigation.rb +37 -0
  86. data/app/components/uchi/ui/no_value/no_value.html.erb +1 -0
  87. data/app/components/uchi/ui/no_value.rb +8 -0
  88. data/app/components/uchi/ui/page_header/page_header.html.erb +7 -2
  89. data/app/components/uchi/ui/pagination/current_link.html.erb +1 -1
  90. data/app/components/uchi/ui/pagination/gap.html.erb +7 -1
  91. data/app/components/uchi/ui/pagination/link.html.erb +1 -1
  92. data/app/components/uchi/ui/pagination/next_link.html.erb +18 -3
  93. data/app/components/uchi/ui/pagination/pagination.html.erb +3 -1
  94. data/app/components/uchi/ui/pagination/previous_link.html.erb +18 -3
  95. data/app/components/uchi/ui/pagination.rb +1 -1
  96. data/app/components/uchi/ui/show/attribute_fields/attribute_fields.html.erb +4 -3
  97. data/app/components/uchi/ui/spinner/spinner.html.erb +17 -3
  98. data/app/controllers/uchi/actions/executions_controller.rb +107 -0
  99. data/app/controllers/uchi/repository_controller.rb +21 -1
  100. data/app/views/layouts/uchi/_flash_messages.html.erb +1 -1
  101. data/app/views/layouts/uchi/application.html.erb +10 -16
  102. data/app/views/uchi/navigation/_main.html.erb +9 -0
  103. data/app/views/uchi/repository/edit.html.erb +2 -2
  104. data/app/views/uchi/repository/index.html.erb +12 -3
  105. data/app/views/uchi/repository/new.html.erb +2 -2
  106. data/app/views/uchi/repository/show.html.erb +13 -2
  107. data/lib/generators/uchi/controller/templates/controller.rb.tt +0 -5
  108. data/lib/generators/uchi/scaffold/scaffold_generator.rb +15 -0
  109. data/lib/uchi/action.rb +84 -0
  110. data/lib/uchi/action_response.rb +108 -0
  111. data/lib/uchi/repository/translate.rb +14 -0
  112. data/lib/uchi/repository.rb +25 -0
  113. data/lib/uchi/version.rb +1 -1
  114. data/lib/uchi.rb +2 -0
  115. metadata +63 -50
  116. data/app/components/flowbite/button/outline.rb +0 -22
  117. data/app/components/flowbite/button/pill.rb +0 -40
  118. data/app/components/flowbite/button.rb +0 -92
  119. data/app/components/flowbite/card.rb +0 -45
  120. data/app/components/flowbite/input/file.rb +0 -30
  121. data/app/components/flowbite/input/textarea.rb +0 -42
  122. data/app/components/flowbite/input/validation_error.rb +0 -11
  123. data/app/components/flowbite/input_field/checkbox.html.erb +0 -14
  124. data/app/components/flowbite/input_field/input_field.html.erb +0 -8
  125. data/app/components/flowbite/input_field/radio_button.html.erb +0 -14
  126. data/app/components/flowbite/link.rb +0 -21
  127. data/app/components/flowbite/toast/toast.html.erb +0 -11
  128. /data/app/components/{flowbite → uchi/flowbite}/toast/icon.html.erb +0 -0
@@ -10,12 +10,12 @@ running the following command:
10
10
  /* We need tailwindcss, duh */
11
11
  @import "tailwindcss";
12
12
 
13
- /* Import the default theme variables from Flowbite
14
- This imports them from node_modules/flowbite/src/themes/default.css */
15
- @import "flowbite/src/themes/default";
16
-
17
13
  /* Import the Flowbite plugin */
18
14
  @plugin "flowbite/plugin";
19
15
 
16
+ /* Import the default theme variables from Flowbite */
17
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
18
+ @import "flowbite/src/themes/default"; /* This imports them from node_modules/flowbite/src/themes/default.css */
19
+
20
20
  /* Configure the source files of Flowbite */
21
21
  @source "../../../node_modules/flowbite";
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::Select.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::Select.new(**options)) %>
@@ -1 +1 @@
1
- <%= field.value(record) %>
1
+ <%= label_for_associated_record %>
@@ -1,5 +1,3 @@
1
- <% if associated_record %>
2
- <%= render(Flowbite::Link.new(
3
- href: associated_repository.routes.path_for(:show, id: associated_record.id)
4
- ).with_content(associated_record.to_s)) %>
5
- <% end %>
1
+ <%= render(Uchi::Flowbite::Link.new(
2
+ href: path_to_show_associated_record,
3
+ ).with_content(label_for_associated_record)) %>
@@ -5,6 +5,23 @@ module Uchi
5
5
  class BelongsTo < Field
6
6
  DEFAULT_COLLECTION_QUERY = ->(query) { query }.freeze
7
7
 
8
+ module Helpers
9
+ def associated_record
10
+ field.value(record)
11
+ end
12
+
13
+ def associated_repository
14
+ reflection = record.class.reflect_on_association(field.name)
15
+ model = reflection.klass
16
+ repository_class = Uchi::Repository.for_model(model)
17
+ repository_class.new
18
+ end
19
+
20
+ def label_for_associated_record
21
+ associated_repository.title(associated_record)
22
+ end
23
+ end
24
+
8
25
  class Edit < Uchi::Field::Base::Edit
9
26
  def associated_repository
10
27
  model = reflection.klass
@@ -54,18 +71,24 @@ module Uchi
54
71
  end
55
72
 
56
73
  class Index < Uchi::Field::Base::Index
74
+ include Helpers
75
+
76
+ def render?
77
+ associated_record.present?
78
+ end
57
79
  end
58
80
 
59
81
  class Show < Uchi::Field::Base::Show
60
- def associated_record
61
- field.value(record)
82
+ include Helpers
83
+
84
+ def path_to_show_associated_record
85
+ associated_repository
86
+ .routes
87
+ .path_for(:show, id: associated_record.id)
62
88
  end
63
89
 
64
- def associated_repository
65
- reflection = record.class.reflect_on_association(field.name)
66
- model = reflection.klass
67
- repository_class = Uchi::Repository.for_model(model)
68
- repository_class.new
90
+ def render?
91
+ associated_record.present?
69
92
  end
70
93
  end
71
94
 
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::Checkbox.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::Checkbox.new(**options)) %>
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::Date.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::Date.new(**options)) %>
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::DateTime.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::DateTime.new(**options)) %>
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::File.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::File.new(**options)) %>
@@ -1,6 +1,7 @@
1
1
  <% attachment = field.value(record) %>
2
+
2
3
  <% if attachment.attached? %>
3
4
  <%= attachment.filename.to_s %>
4
5
  <% else %>
5
- <span class="text-gray-400">—</span>
6
+ <%= render(Uchi::Ui::NoValue.new) %>
6
7
  <% end %>
@@ -1,8 +1,9 @@
1
1
  <% attachment = field.value(record) %>
2
+
2
3
  <% if attachment.attached? %>
3
- <%= render Flowbite::Link.new(href: Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true)) do %>
4
+ <%= render Uchi::Flowbite::Link.new(href: Rails.application.routes.url_helpers.rails_blob_path(attachment, only_path: true)) do %>
4
5
  <%= attachment.filename %>
5
6
  <% end %>
6
7
  <% else %>
7
- <span class="text-gray-400">—</span>
8
+ <%= render(Uchi::Ui::NoValue.new) %>
8
9
  <% end %>
@@ -1,5 +1,7 @@
1
1
  <div class="my-10">
2
- <h2 class="px-4 my-6 text-2xl font-bold md:px-0"><%= repository.translate.field_label(field) %></h2>
2
+ <h2 class="px-4 my-6 text-2xl font-semibold tracking-tight text-heading md:px-0">
3
+ <%= repository.translate.field_label(field) %>
4
+ </h2>
3
5
 
4
6
  <%= render(Uchi::Ui::Index::TurboFrame.new(
5
7
  repository: associated_repository,
@@ -19,10 +21,10 @@
19
21
  },
20
22
  )
21
23
  )) do %>
22
- <div class="relative overflow-x-auto bg-white border border-gray-200 shadow-sm md:rounded-lg dark:bg-gray-800 dark:border-gray-700">
24
+ <%= render(Uchi::Ui::Frame.new) do %>
23
25
  <div class="grid grid place-items-center p-8">
24
26
  <%= render(Uchi::Ui::Spinner.new(message: repository.translate.loading_message)) %>
25
27
  </div>
26
- </div>
28
+ <% end %>
27
29
  <% end %>
28
30
  </div>
@@ -1,5 +1,7 @@
1
1
  <div class="my-10">
2
- <h2 class="px-4 my-6 text-2xl font-bold md:px-0"><%= repository.translate.field_label(field) %></h2>
2
+ <h2 class="px-4 my-6 text-2xl font-semibold tracking-tight text-heading md:px-0">
3
+ <%= repository.translate.field_label(field) %>
4
+ </h2>
3
5
 
4
6
  <%= render(Uchi::Ui::Index::TurboFrame.new(
5
7
  repository: associated_repository,
@@ -19,10 +21,10 @@
19
21
  },
20
22
  )
21
23
  )) do %>
22
- <div class="relative overflow-x-auto bg-white border border-gray-200 shadow-sm md:rounded-lg dark:bg-gray-800 dark:border-gray-700">
24
+ <%= render(Uchi::Ui::Frame.new) do %>
23
25
  <div class="grid grid place-items-center p-8">
24
26
  <%= render(Uchi::Ui::Spinner.new(message: repository.translate.loading_message)) %>
25
27
  </div>
26
- </div>
28
+ <% end %>
27
29
  <% end %>
28
30
  </div>
@@ -1,4 +1,4 @@
1
- <%= render(Flowbite::Link.new(
1
+ <%= render(Uchi::Flowbite::Link.new(
2
2
  href: @repository.routes.path_for(:show, id: record.id),
3
3
  target: "_top").with_content(field.value(record))
4
4
  ) %>
@@ -1,4 +1,4 @@
1
- <%= render(Flowbite::Link.new(
1
+ <%= render(Uchi::Flowbite::Link.new(
2
2
  href: @repository.routes.path_for(:show, id: record.id),
3
3
  target: "_top").with_content(field.value(record))
4
4
  ) %>
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::File.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::File.new(**options)) %>
@@ -1,6 +1,7 @@
1
1
  <% attachment = field.value(record) %>
2
+
2
3
  <% if attachment.attached? %>
3
4
  <%= image_tag attachment.variant(resize_to_limit: [100, 100]), class: "h-12 w-12 object-cover rounded" %>
4
5
  <% else %>
5
- <span class="text-gray-400">—</span>
6
+ <%= render(Uchi::Ui::NoValue.new) %>
6
7
  <% end %>
@@ -1,6 +1,7 @@
1
1
  <% attachment = field.value(record) %>
2
+
2
3
  <% if attachment.attached? %>
3
4
  <%= image_tag attachment, class: "max-w-full h-auto rounded-lg shadow-md" %>
4
5
  <% else %>
5
- <span class="text-gray-400">—</span>
6
+ <%= render(Uchi::Ui::NoValue.new) %>
6
7
  <% end %>
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::Number.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::Number.new(**options)) %>
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::Text.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::Text.new(**options)) %>
@@ -1 +1 @@
1
- <%= render(Flowbite::InputField::Textarea.new(**options)) %>
1
+ <%= render(Uchi::Flowbite::InputField::Textarea.new(**options)) %>
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Flowbite
3
+ module Uchi::Flowbite
4
4
  # Renders a breadcrumb navigation component.
5
5
  #
6
6
  # See https://flowbite.com/docs/components/breadcrumb/
7
7
  #
8
8
  # @example Basic usage with BreadcrumbItem components
9
- # <%= render Flowbite::Breadcrumb.new do |breadcrumb| %>
9
+ # <%= render Uchi::Flowbite::Breadcrumb.new do |breadcrumb| %>
10
10
  # <% breadcrumb.with_item do %>
11
- # <%= render Flowbite::BreadcrumbItem::First.new(href: "/") { "Home" } %>
11
+ # <%= render Uchi::Flowbite::BreadcrumbItem::First.new(href: "/") { "Home" } %>
12
12
  # <% end %>
13
13
  # <% breadcrumb.with_item do %>
14
- # <%= render Flowbite::BreadcrumbItem.new(href: "/projects") { "Projects" } %>
14
+ # <%= render Uchi::Flowbite::BreadcrumbItem.new(href: "/projects") { "Projects" } %>
15
15
  # <% end %>
16
16
  # <% breadcrumb.with_item do %>
17
- # <%= render Flowbite::BreadcrumbItem::Current.new { "Current Page" } %>
17
+ # <%= render Uchi::Flowbite::BreadcrumbItem::Current.new { "Current Page" } %>
18
18
  # <% end %>
19
19
  # <% end %>
20
20
  class Breadcrumb < ViewComponent::Base
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Flowbite
3
+ module Uchi::Flowbite
4
4
  # Renders a breadcrumb home icon.
5
5
  #
6
6
  # This is typically used as a prefix icon in the first breadcrumb item,
7
7
  # but can be used standalone if needed.
8
8
  #
9
9
  # @example Standalone usage
10
- # <%= render Flowbite::BreadcrumbHome.new %>
10
+ # <%= render Uchi::Flowbite::BreadcrumbHome.new %>
11
11
  class BreadcrumbHome < ViewComponent::Base
12
12
  def call
13
13
  tag.svg(
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Flowbite
3
+ module Uchi::Flowbite
4
4
  class BreadcrumbItem
5
5
  # Renders the current page breadcrumb item.
6
6
  # Current items are rendered as non-interactive spans with different styling.
@@ -8,7 +8,7 @@ module Flowbite
8
8
  # @param options [Hash] Additional HTML attributes to pass to the span element.
9
9
  #
10
10
  # @example Current page item
11
- # <%= render Flowbite::BreadcrumbItem::Current.new { "Current Page" } %>
11
+ # <%= render Uchi::Flowbite::BreadcrumbItem::Current.new { "Current Page" } %>
12
12
  class Current < BreadcrumbItem
13
13
  def initialize(**options)
14
14
  super(href: nil, **options)
@@ -26,7 +26,7 @@ module Flowbite
26
26
  end
27
27
 
28
28
  def link_classes
29
- ["ms-1", "text-sm", "font-medium", "text-gray-500", "dark:text-gray-400"]
29
+ ["ms-1", "text-sm", "font-medium", "text-body-subtle"]
30
30
  end
31
31
  end
32
32
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Flowbite
3
+ module Uchi::Flowbite
4
4
  class BreadcrumbItem
5
5
  # Renders the first breadcrumb item (typically home).
6
6
  # First items don't show a separator icon.
@@ -9,7 +9,7 @@ module Flowbite
9
9
  # @param options [Hash] Additional HTML attributes to pass to the link element.
10
10
  #
11
11
  # @example First item
12
- # <%= render Flowbite::BreadcrumbItem::First.new(href: "/") { "Home" } %>
12
+ # <%= render Uchi::Flowbite::BreadcrumbItem::First.new(href: "/") { "Home" } %>
13
13
  class First < BreadcrumbItem
14
14
  protected
15
15
 
@@ -18,7 +18,7 @@ module Flowbite
18
18
  end
19
19
 
20
20
  def link_classes
21
- ["text-sm", "font-medium", "inline-flex", "items-center", "text-gray-700", "hover:text-blue-600", "dark:text-gray-400", "dark:hover:text-white"]
21
+ ["text-sm", "font-medium", "inline-flex", "items-center", "text-body", "hover:text-fg-brand"]
22
22
  end
23
23
 
24
24
  def prefix_icon
@@ -26,7 +26,7 @@ module Flowbite
26
26
  end
27
27
 
28
28
  def render_link
29
- icon = render(Flowbite::BreadcrumbHome.new)
29
+ icon = render(Uchi::Flowbite::BreadcrumbHome.new)
30
30
  link_options = {class: link_classes}.merge(options)
31
31
  content_tag(:a, safe_join([icon, content]), href: href, **link_options)
32
32
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Flowbite
3
+ module Uchi::Flowbite
4
4
  # Base class for rendering a breadcrumb item (middle items in the breadcrumb trail).
5
5
  #
6
6
  # @param href [String] The URL for the breadcrumb link.
7
7
  # @param options [Hash] Additional HTML attributes to pass to the link element.
8
8
  #
9
9
  # @example Middle item
10
- # <%= render Flowbite::BreadcrumbItem.new(href: "/projects") { "Projects" } %>
10
+ # <%= render Uchi::Flowbite::BreadcrumbItem.new(href: "/projects") { "Projects" } %>
11
11
  class BreadcrumbItem < ViewComponent::Base
12
12
  attr_reader :href, :options
13
13
 
@@ -33,7 +33,7 @@ module Flowbite
33
33
  end
34
34
 
35
35
  def prefix_icon
36
- Flowbite::BreadcrumbSeparator.new
36
+ Uchi::Flowbite::BreadcrumbSeparator.new
37
37
  end
38
38
 
39
39
  def render_link
@@ -42,7 +42,7 @@ module Flowbite
42
42
  end
43
43
 
44
44
  def link_classes
45
- ["ms-1", "text-sm", "font-medium", "text-gray-700", "hover:text-blue-600", "dark:text-gray-400", "dark:hover:text-white"]
45
+ ["ms-1", "text-sm", "font-medium", "text-body", "hover:text-fg-brand"]
46
46
  end
47
47
  end
48
48
  end
@@ -1,28 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Flowbite
3
+ module Uchi::Flowbite
4
4
  # Renders a breadcrumb separator icon.
5
5
  #
6
6
  # This is automatically used by BreadcrumbItem components, but can be
7
7
  # used standalone if needed.
8
8
  #
9
9
  # @example Standalone usage
10
- # <%= render Flowbite::BreadcrumbSeparator.new %>
10
+ # <%= render Uchi::Flowbite::BreadcrumbSeparator.new %>
11
11
  class BreadcrumbSeparator < ViewComponent::Base
12
12
  def call
13
13
  tag.svg(
14
- class: "rtl:rotate-180 w-3 h-3 text-gray-400 mx-1",
14
+ class: "w-3.5 h-3.5 rtl:rotate-180 text-body",
15
15
  "aria-hidden": "true",
16
16
  xmlns: "http://www.w3.org/2000/svg",
17
17
  fill: "none",
18
- viewBox: "0 0 6 10"
18
+ height: 24,
19
+ viewBox: "0 0 24 24",
20
+ width: 24
19
21
  ) do
20
22
  tag.path(
21
23
  stroke: "currentColor",
22
24
  "stroke-linecap": "round",
23
25
  "stroke-linejoin": "round",
24
26
  "stroke-width": "2",
25
- d: "m1 9 4-4-4-4"
27
+ d: "m9 5 7 7-7 7"
26
28
  )
27
29
  end
28
30
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uchi::Flowbite
4
+ class Button
5
+ class Outline < Uchi::Flowbite::Button
6
+ class << self
7
+ # rubocop:disable Layout/LineLength, Metrics/MethodLength
8
+ def styles
9
+ {
10
+ danger: Uchi::Flowbite::Style.new(
11
+ default: ["focus:outline-none", "text-danger", "bg-transparent", "box-border", "border", "border-danger", "hover:text-white", "hover:bg-danger-strong", "focus:ring-4", "focus:ring-danger-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
12
+ ),
13
+ dark: Uchi::Flowbite::Style.new(
14
+ default: ["focus:outline-none", "text-dark", "bg-transparent", "box-border", "border", "border-dark", "hover:text-white", "hover:bg-dark-strong", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
15
+ ),
16
+ default: Uchi::Flowbite::Style.new(
17
+ default: ["focus:outline-none", "text-brand", "bg-transparent", "box-border", "border", "border-brand", "hover:text-white", "hover:bg-brand-strong", "focus:ring-4", "focus:ring-brand-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
18
+ ),
19
+ secondary: Uchi::Flowbite::Style.new(
20
+ default: ["focus:outline-none", "text-body", "bg-transparent", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
21
+ ),
22
+ success: Uchi::Flowbite::Style.new(
23
+ default: ["focus:outline-none", "text-success", "bg-transparent", "box-border", "border", "border-success", "hover:text-white", "hover:bg-success-strong", "focus:ring-4", "focus:ring-success-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
24
+ ),
25
+ tertiary: Uchi::Flowbite::Style.new(
26
+ default: ["focus:outline-none", "text-body", "bg-transparent", "box-border", "border", "border-default", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary-soft", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
27
+ ),
28
+ warning: Uchi::Flowbite::Style.new(
29
+ default: ["focus:outline-none", "text-warning", "bg-transparent", "box-border", "border", "border-warning", "hover:text-white", "hover:bg-warning-strong", "focus:ring-4", "focus:ring-warning-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base", "text-center", "me-2", "mb-2"]
30
+ )
31
+ }
32
+ end
33
+ # rubocop:enable Layout/LineLength, Metrics/MethodLength
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uchi::Flowbite
4
+ class Button
5
+ class Pill < Uchi::Flowbite::Button
6
+ class << self
7
+ # rubocop:disable Layout/LineLength, Metrics/MethodLength
8
+ def styles
9
+ {
10
+ danger: Uchi::Flowbite::Style.new(
11
+ default: ["focus:outline-none", "text-white", "bg-danger", "box-border", "border", "border-transparent", "hover:bg-danger-strong", "focus:ring-4", "focus:ring-danger-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
12
+ ),
13
+ dark: Uchi::Flowbite::Style.new(
14
+ default: ["focus:outline-none", "text-white", "bg-dark", "box-border", "border", "border-transparent", "hover:bg-dark-strong", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
15
+ ),
16
+ default: Uchi::Flowbite::Style.new(
17
+ default: ["focus:outline-none", "text-white", "bg-brand", "box-border", "border", "border-transparent", "hover:bg-brand-strong", "focus:ring-4", "focus:ring-brand-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
18
+ ),
19
+ ghost: Uchi::Flowbite::Style.new(
20
+ default: ["focus:outline-none", "text-heading", "bg-transparent", "box-border", "border", "border-transparent", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "font-medium", "leading-5", "rounded-full", "text-center"]
21
+ ),
22
+ secondary: Uchi::Flowbite::Style.new(
23
+ default: ["focus:outline-none", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
24
+ ),
25
+ success: Uchi::Flowbite::Style.new(
26
+ default: ["focus:outline-none", "text-white", "bg-success", "box-border", "border", "border-transparent", "hover:bg-success-strong", "focus:ring-4", "focus:ring-success-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
27
+ ),
28
+ tertiary: Uchi::Flowbite::Style.new(
29
+ default: ["focus:outline-none", "text-body", "bg-neutral-primary-soft", "box-border", "border", "border-default", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary-soft", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
30
+ ),
31
+ warning: Uchi::Flowbite::Style.new(
32
+ default: ["focus:outline-none", "text-white", "bg-warning", "box-border", "border", "border-transparent", "hover:bg-warning-strong", "focus:ring-4", "focus:ring-warning-medium", "shadow-xs", "font-medium", "leading-5", "rounded-full", "text-center"]
33
+ )
34
+ }
35
+ end
36
+ # rubocop:enable Layout/LineLength, Metrics/MethodLength
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uchi::Flowbite
4
+ # Renders a HTML button element.
5
+ #
6
+ # See https://flowbite.com/docs/components/buttons/
7
+ #
8
+ # @param label [String] The text to display on the button.
9
+ #
10
+ # All other parameters are optional and are passed directly to the button tag
11
+ # as HTML attributes.
12
+ class Button < ViewComponent::Base
13
+ SIZES = {
14
+ xs: ["text-xs", "px-3", "py-1.5"],
15
+ sm: ["text-sm", "px-3", "py-2"],
16
+ default: ["text-sm", "px-4", "py-2.5"],
17
+ lg: ["text-base", "px-5", "py-3"],
18
+ xl: ["text-base", "px-6", "py-3.5"]
19
+ }.freeze
20
+
21
+ class << self
22
+ def classes(size: :default, state: :default, style: :default)
23
+ style = styles.fetch(style) or raise "wut"
24
+ classes = style.fetch(state)
25
+ classes + sizes.fetch(size)
26
+ end
27
+
28
+ def sizes
29
+ SIZES
30
+ end
31
+
32
+ # rubocop:disable Layout/LineLength
33
+ def styles
34
+ {
35
+ danger: Uchi::Flowbite::Style.new(
36
+ default: ["focus:outline-none", "text-white", "bg-danger", "box-border", "border", "border-transparent", "hover:bg-danger-strong", "focus:ring-4", "focus:ring-danger-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
37
+ ),
38
+ dark: Uchi::Flowbite::Style.new(
39
+ default: ["focus:outline-none", "text-white", "bg-dark", "box-border", "border", "border-transparent", "hover:bg-dark-strong", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
40
+ ),
41
+ default: Uchi::Flowbite::Style.new(
42
+ default: ["focus:outline-none", "text-white", "bg-brand", "box-border", "border", "border-transparent", "hover:bg-brand-strong", "focus:ring-4", "focus:ring-brand-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
43
+ ),
44
+ ghost: Uchi::Flowbite::Style.new(
45
+ default: ["focus:outline-none", "text-heading", "bg-transparent", "box-border", "border", "border-transparent", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "font-medium", "leading-5", "rounded-base"]
46
+ ),
47
+ secondary: Uchi::Flowbite::Style.new(
48
+ default: ["focus:outline-none", "text-body", "bg-neutral-secondary-medium", "box-border", "border", "border-default-medium", "hover:bg-neutral-tertiary-medium", "focus:ring-4", "focus:ring-neutral-tertiary", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
49
+ ),
50
+ success: Uchi::Flowbite::Style.new(
51
+ default: ["focus:outline-none", "text-white", "bg-success", "box-border", "border", "border-transparent", "hover:bg-success-strong", "focus:ring-4", "focus:ring-success-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
52
+ ),
53
+ tertiary: Uchi::Flowbite::Style.new(
54
+ default: ["focus:outline-none", "text-body", "bg-neutral-primary-soft", "box-border", "border", "border-default", "hover:bg-neutral-secondary-medium", "focus:ring-4", "focus:ring-neutral-tertiary-soft", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
55
+ ),
56
+ warning: Uchi::Flowbite::Style.new(
57
+ default: ["focus:outline-none", "text-white", "bg-warning", "box-border", "border", "border-transparent", "hover:bg-warning-strong", "focus:ring-4", "focus:ring-warning-medium", "shadow-xs", "font-medium", "leading-5", "rounded-base"]
58
+ )
59
+ }.freeze
60
+ end
61
+ # rubocop:enable Layout/LineLength
62
+ end
63
+
64
+ attr_reader :button_attributes, :size, :style
65
+
66
+ def initialize(class: nil, size: :default, style: :default, **button_attributes)
67
+ @class = Array.wrap(binding.local_variable_get(:class))
68
+ @size = size
69
+ @style = style
70
+ @button_attributes = button_attributes
71
+ end
72
+
73
+ def call
74
+ content_tag(
75
+ :button,
76
+ content,
77
+ **options
78
+ )
79
+ end
80
+
81
+ private
82
+
83
+ def classes
84
+ self.class.classes(size: size, state: :default, style: style) + @class
85
+ end
86
+
87
+ def options
88
+ {
89
+ class: classes
90
+ }.merge(button_attributes)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,7 @@
1
+ <%= content_tag(:div, card_options) do %>
2
+ <%= title %>
3
+
4
+ <% if content.present? %>
5
+ <div class="text-body"><%= content %></div>
6
+ <% end %>
7
+ <% end %>
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uchi::Flowbite
4
+ class Card
5
+ # Renders the title of a card element.
6
+ class Title < ViewComponent::Base
7
+ class << self
8
+ def classes(state: :default, style: :default)
9
+ style = styles.fetch(style)
10
+ style.fetch(state)
11
+ end
12
+
13
+ # rubocop:disable Layout/LineLength
14
+ def styles
15
+ {
16
+ default: Uchi::Flowbite::Style.new(
17
+ default: ["mb-2", "text-2xl", "font-semibold", "tracking-tight", "text-heading"]
18
+ )
19
+ }.freeze
20
+ end
21
+ # rubocop:enable Layout/LineLength
22
+ end
23
+
24
+ def call
25
+ title_options = {
26
+ class: self.class.classes
27
+ }.merge(@options)
28
+
29
+ content_tag(:h5, content, **title_options)
30
+ end
31
+
32
+ def initialize(**options)
33
+ @options = options || {}
34
+ end
35
+ end
36
+ end
37
+ end