uchi 0.1.5 → 0.1.7

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -0
  3. data/app/assets/javascripts/controllers/fields/belongs_to_controller.js +130 -0
  4. data/app/assets/javascripts/controllers/fields/has_many_controller.js +146 -0
  5. data/app/assets/javascripts/uchi/application.js +3456 -24
  6. data/app/assets/javascripts/uchi.js +20 -0
  7. data/app/assets/stylesheets/uchi/application.css +676 -2473
  8. data/app/assets/tailwind/uchi.css +6 -6
  9. data/app/components/uchi/field/belongs_to/edit.html.erb +73 -1
  10. data/app/components/uchi/field/belongs_to/index.html.erb +1 -1
  11. data/app/components/uchi/field/belongs_to/show.html.erb +3 -5
  12. data/app/components/uchi/field/belongs_to.rb +53 -30
  13. data/app/components/uchi/field/boolean/edit.html.erb +1 -1
  14. data/app/components/uchi/field/date/edit.html.erb +1 -1
  15. data/app/components/uchi/field/date_time/edit.html.erb +1 -1
  16. data/app/components/uchi/field/file/edit.html.erb +1 -1
  17. data/app/components/uchi/field/file/index.html.erb +2 -1
  18. data/app/components/uchi/field/file/show.html.erb +3 -2
  19. data/app/components/uchi/field/has_and_belongs_to_many/show.html.erb +6 -4
  20. data/app/components/uchi/field/has_many/edit.html.erb +86 -1
  21. data/app/components/uchi/field/has_many/show.html.erb +6 -4
  22. data/app/components/uchi/field/has_many.rb +59 -11
  23. data/app/components/uchi/field/id/index.html.erb +1 -1
  24. data/app/components/uchi/field/id/show.html.erb +1 -1
  25. data/app/components/uchi/field/image/edit.html.erb +1 -1
  26. data/app/components/uchi/field/image/index.html.erb +2 -1
  27. data/app/components/uchi/field/image/show.html.erb +2 -1
  28. data/app/components/uchi/field/number/edit.html.erb +1 -1
  29. data/app/components/uchi/field/string/edit.html.erb +1 -1
  30. data/app/components/uchi/field/text/edit.html.erb +1 -1
  31. data/app/components/{flowbite → uchi/flowbite}/breadcrumb.rb +5 -5
  32. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_home.rb +2 -2
  33. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item/current.rb +3 -3
  34. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item/first.rb +4 -4
  35. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_item.rb +4 -4
  36. data/app/components/{flowbite → uchi/flowbite}/breadcrumb_separator.rb +7 -5
  37. data/app/components/uchi/flowbite/button/outline.rb +37 -0
  38. data/app/components/uchi/flowbite/button/pill.rb +40 -0
  39. data/app/components/uchi/flowbite/button.rb +93 -0
  40. data/app/components/uchi/flowbite/card/card.html.erb +7 -0
  41. data/app/components/uchi/flowbite/card/title.rb +37 -0
  42. data/app/components/uchi/flowbite/card.rb +84 -0
  43. data/app/components/{flowbite → uchi/flowbite}/input/checkbox.rb +7 -7
  44. data/app/components/{flowbite → uchi/flowbite}/input/date.rb +1 -1
  45. data/app/components/{flowbite → uchi/flowbite}/input/date_time.rb +1 -1
  46. data/app/components/{flowbite → uchi/flowbite}/input/email.rb +1 -1
  47. data/app/components/{flowbite → uchi/flowbite}/input/field.rb +11 -10
  48. data/app/components/uchi/flowbite/input/file.rb +30 -0
  49. data/app/components/{flowbite → uchi/flowbite}/input/hint.rb +6 -5
  50. data/app/components/{flowbite → uchi/flowbite}/input/label.rb +8 -7
  51. data/app/components/{flowbite → uchi/flowbite}/input/number.rb +1 -1
  52. data/app/components/{flowbite → uchi/flowbite}/input/password.rb +1 -1
  53. data/app/components/{flowbite → uchi/flowbite}/input/phone.rb +1 -1
  54. data/app/components/{flowbite → uchi/flowbite}/input/radio_button.rb +7 -7
  55. data/app/components/{flowbite → uchi/flowbite}/input/select.rb +16 -7
  56. data/app/components/uchi/flowbite/input/textarea.rb +42 -0
  57. data/app/components/{flowbite → uchi/flowbite}/input/url.rb +1 -1
  58. data/app/components/uchi/flowbite/input/validation_error.rb +39 -0
  59. data/app/components/uchi/flowbite/input_field/checkbox.html.erb +9 -0
  60. data/app/components/{flowbite → uchi/flowbite}/input_field/checkbox.rb +10 -6
  61. data/app/components/{flowbite → uchi/flowbite}/input_field/date.rb +2 -2
  62. data/app/components/{flowbite → uchi/flowbite}/input_field/date_time.rb +2 -2
  63. data/app/components/{flowbite → uchi/flowbite}/input_field/email.rb +2 -2
  64. data/app/components/{flowbite → uchi/flowbite}/input_field/file.rb +2 -2
  65. data/app/components/uchi/flowbite/input_field/input_field.html.erb +9 -0
  66. data/app/components/{flowbite → uchi/flowbite}/input_field/number.rb +2 -2
  67. data/app/components/{flowbite → uchi/flowbite}/input_field/password.rb +2 -2
  68. data/app/components/{flowbite → uchi/flowbite}/input_field/phone.rb +2 -2
  69. data/app/components/uchi/flowbite/input_field/radio_button.html.erb +9 -0
  70. data/app/components/{flowbite → uchi/flowbite}/input_field/radio_button.rb +13 -9
  71. data/app/components/{flowbite → uchi/flowbite}/input_field/select.rb +7 -3
  72. data/app/components/{flowbite → uchi/flowbite}/input_field/text.rb +1 -1
  73. data/app/components/{flowbite → uchi/flowbite}/input_field/textarea.rb +2 -2
  74. data/app/components/{flowbite → uchi/flowbite}/input_field/url.rb +2 -2
  75. data/app/components/{flowbite → uchi/flowbite}/input_field.rb +19 -5
  76. data/app/components/uchi/flowbite/link.rb +41 -0
  77. data/app/components/{flowbite → uchi/flowbite}/style.rb +1 -1
  78. data/app/components/{flowbite → uchi/flowbite}/toast/icon.rb +1 -1
  79. data/app/components/uchi/flowbite/toast/toast.html.erb +40 -0
  80. data/app/components/{flowbite → uchi/flowbite}/toast.rb +2 -2
  81. data/app/components/uchi/ui/actions/dropdown/dropdown.html.erb +59 -0
  82. data/app/components/uchi/ui/actions/dropdown.rb +32 -0
  83. data/app/components/uchi/ui/breadcrumb/breadcrumb.html.erb +4 -4
  84. data/app/components/uchi/ui/form/input/collection_checkboxes.html.erb +2 -1
  85. data/app/components/uchi/ui/form/input/collection_checkboxes.rb +12 -17
  86. data/app/components/uchi/ui/frame/frame.html.erb +6 -1
  87. data/app/components/uchi/ui/index/records_table/records_table.html.erb +109 -18
  88. data/app/components/uchi/ui/index/records_table/search_form/search_form.html.erb +21 -5
  89. data/app/components/uchi/ui/navigation/navigation.html.erb +9 -0
  90. data/app/components/uchi/ui/navigation.rb +37 -0
  91. data/app/components/uchi/ui/no_value/no_value.html.erb +1 -0
  92. data/app/components/uchi/ui/no_value.rb +8 -0
  93. data/app/components/uchi/ui/page_header/page_header.html.erb +13 -8
  94. data/app/components/uchi/ui/pagination/current_link.html.erb +1 -1
  95. data/app/components/uchi/ui/pagination/gap.html.erb +7 -1
  96. data/app/components/uchi/ui/pagination/link.html.erb +1 -1
  97. data/app/components/uchi/ui/pagination/next_link.html.erb +18 -3
  98. data/app/components/uchi/ui/pagination/pagination.html.erb +3 -1
  99. data/app/components/uchi/ui/pagination/previous_link.html.erb +18 -3
  100. data/app/components/uchi/ui/pagination.rb +1 -1
  101. data/app/components/uchi/ui/show/attribute_fields/attribute_fields.html.erb +4 -3
  102. data/app/components/uchi/ui/spinner/spinner.html.erb +17 -3
  103. data/app/controllers/uchi/actions/executions_controller.rb +107 -0
  104. data/app/controllers/uchi/belongs_to/associated_records_controller.rb +89 -0
  105. data/app/controllers/uchi/has_many/associated_records_controller.rb +89 -0
  106. data/app/controllers/uchi/repository_controller.rb +21 -1
  107. data/app/views/layouts/uchi/_flash_messages.html.erb +1 -1
  108. data/app/views/layouts/uchi/_javascript.html.erb +1 -0
  109. data/app/views/layouts/uchi/_stylesheets.html.erb +1 -0
  110. data/app/views/layouts/uchi/application.html.erb +13 -19
  111. data/app/views/uchi/belongs_to/associated_records/index.html.erb +13 -0
  112. data/app/views/uchi/has_many/associated_records/index.html.erb +26 -0
  113. data/app/views/uchi/navigation/_main.html.erb +92 -0
  114. data/app/views/uchi/repository/edit.html.erb +2 -2
  115. data/app/views/uchi/repository/index.html.erb +12 -3
  116. data/app/views/uchi/repository/new.html.erb +2 -2
  117. data/app/views/uchi/repository/show.html.erb +13 -2
  118. data/lib/generators/uchi/controller/controller_generator.rb +0 -4
  119. data/lib/generators/uchi/controller/templates/controller.rb.tt +0 -5
  120. data/lib/generators/uchi/install/install_generator.rb +1 -1
  121. data/lib/generators/uchi/scaffold/scaffold_generator.rb +15 -0
  122. data/lib/uchi/action.rb +84 -0
  123. data/lib/uchi/action_response.rb +108 -0
  124. data/lib/uchi/field/configuration.rb +1 -1
  125. data/lib/uchi/repository/translate.rb +14 -0
  126. data/lib/uchi/repository.rb +38 -2
  127. data/lib/uchi/routes.rb +45 -0
  128. data/lib/uchi/version.rb +1 -1
  129. data/lib/uchi.rb +6 -1
  130. metadata +72 -50
  131. data/app/components/flowbite/button/outline.rb +0 -22
  132. data/app/components/flowbite/button/pill.rb +0 -40
  133. data/app/components/flowbite/button.rb +0 -92
  134. data/app/components/flowbite/card.rb +0 -45
  135. data/app/components/flowbite/input/file.rb +0 -30
  136. data/app/components/flowbite/input/textarea.rb +0 -42
  137. data/app/components/flowbite/input/validation_error.rb +0 -11
  138. data/app/components/flowbite/input_field/checkbox.html.erb +0 -14
  139. data/app/components/flowbite/input_field/input_field.html.erb +0 -8
  140. data/app/components/flowbite/input_field/radio_button.html.erb +0 -14
  141. data/app/components/flowbite/link.rb +0 -21
  142. data/app/components/flowbite/toast/toast.html.erb +0 -11
  143. /data/app/components/{flowbite → uchi/flowbite}/toast/icon.html.erb +0 -0
@@ -0,0 +1,40 @@
1
+ <div
2
+ class="<%= container_classes.join(" ") %>"
3
+ role="alert"
4
+ <%= options.map { |k, v| "#{k}=\"#{v}\"" }.join(" ").html_safe %>
5
+ >
6
+ <%= render Uchi::Flowbite::Toast::Icon.new(style: style) %>
7
+
8
+ <div class="ms-3 text-sm font-normal"><%= message %></div>
9
+
10
+ <% if dismissible %>
11
+ <%# Styles from https://flowbite.com/docs/components/toast/#default-toast %>
12
+ <button
13
+ type="button"
14
+ class="
15
+ ms-auto flex items-center justify-center text-body
16
+ hover:text-heading bg-transparent box-border border
17
+ border-transparent hover:bg-neutral-secondary-medium focus:ring-4
18
+ focus:ring-neutral-tertiary font-medium leading-5 rounded text-sm
19
+ h-8 w-8 focus:outline-none
20
+ "
21
+ aria-label="Close"
22
+ >
23
+ <svg
24
+ class="w-3 h-3"
25
+ aria-hidden="true"
26
+ xmlns="http://www.w3.org/2000/svg"
27
+ fill="none"
28
+ viewBox="0 0 14 14"
29
+ >
30
+ <path
31
+ stroke="currentColor"
32
+ stroke-linecap="round"
33
+ stroke-linejoin="round"
34
+ stroke-width="2"
35
+ d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
36
+ />
37
+ </svg>
38
+ </button>
39
+ <% end %>
40
+ </div>
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Flowbite
3
+ module Uchi::Flowbite
4
4
  # Renders a toast notification element.
5
5
  #
6
6
  # See https://flowbite.com/docs/components/toast/
@@ -13,7 +13,7 @@ module Flowbite
13
13
  class Toast < ViewComponent::Base
14
14
  class << self
15
15
  def classes
16
- ["flex", "items-center", "w-full", "max-w-xs", "p-4", "text-gray-500", "bg-white", "rounded-lg", "shadow-sm", "dark:text-gray-400", "dark:bg-gray-800"]
16
+ ["flex", "items-center", "w-full", "max-w-xs", "p-4", "text-body", "bg-neutral-primary-soft", "rounded-base", "shadow-xs", "border", "border-default"]
17
17
  end
18
18
  end
19
19
 
@@ -0,0 +1,59 @@
1
+ <div class="relative" data-controller="dropdown">
2
+ <%= render(Uchi::Flowbite::Button.new(style: :secondary, :data => {:action => "dropdown#toggle click@window->dropdown#hide"}, id: button_id)) do %>
3
+ <div class="inline-flex items-center">
4
+ <%= repository.translate.actions_button_label %>
5
+
6
+ <svg
7
+ class="w-4 h-4 ms-1.5 -me-0.5"
8
+ aria-hidden="true"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ width="24"
11
+ height="24"
12
+ fill="none"
13
+ viewBox="0 0 24 24"
14
+ >
15
+ <path
16
+ stroke="currentColor"
17
+ stroke-linecap="round"
18
+ stroke-linejoin="round"
19
+ stroke-width="2"
20
+ d="m19 9-7 7-7-7"
21
+ />
22
+ </svg>
23
+ </div>
24
+ <% end %>
25
+
26
+ <!-- Dropdown menu -->
27
+ <div
28
+ aria-orientation="vertical"
29
+ aria-hidden="true"
30
+ data-dropdown-target="menu"
31
+ class="
32
+ transition transform origin-top-right absolute z-10 hidden bg-neutral-primary-medium border border-default-medium
33
+ rounded-base shadow-lg w-44 mt-1 -ml-1/2
34
+ "
35
+ id="actions-dropdown-<%= record.id %>"
36
+ role="menu"
37
+ tabindex="-1"
38
+ >
39
+ <ul
40
+ class="p-2 text-sm text-body font-medium"
41
+ aria-labelledby="<%= button_id %>"
42
+ >
43
+ <% actions.each do |action| %>
44
+ <li role="menuitem">
45
+ <%= form_with url: helpers.actions_executions_path, method: :post, class: "block" do %>
46
+ <%= hidden_field_tag :model, repository.model.name %>
47
+ <%= hidden_field_tag :action_name, action.class.name %>
48
+ <%= hidden_field_tag :id, record.id %>
49
+
50
+ <%= button_tag type: "submit",
51
+ class: "block inline-flex items-center w-full p-2 hover:bg-neutral-tertiary-medium hover:text-heading rounded" do %>
52
+ <%= action.name %>
53
+ <% end %>
54
+ <% end %>
55
+ </li>
56
+ <% end %>
57
+ </ul>
58
+ </div>
59
+ </div>
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uchi
4
+ module Ui
5
+ module Actions
6
+ # Renders a dropdown menu of actions that can be performed on a record.
7
+ #
8
+ # This component displays available actions for a repository in a dropdown
9
+ # menu. Each action can be clicked to execute it on the specified record(s).
10
+ class Dropdown < ViewComponent::Base
11
+ attr_reader :actions, :record, :repository
12
+
13
+ def initialize(actions:, record:, repository:)
14
+ super()
15
+ @actions = actions
16
+ @record = record
17
+ @repository = repository
18
+ end
19
+
20
+ def render?
21
+ actions.any?
22
+ end
23
+
24
+ private
25
+
26
+ def button_id
27
+ "actions-dropdown-button-#{record.id}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,12 +1,12 @@
1
- <%= render(Flowbite::Breadcrumb.new) do |breadcrumb| %>
1
+ <%= render(Uchi::Flowbite::Breadcrumb.new) do |breadcrumb| %>
2
2
  <% items.each.with_index do |item, index| %>
3
3
  <% breadcrumb.with_item do %>
4
4
  <% if index.zero? %>
5
- <%= render Flowbite::BreadcrumbItem::First.new(href: item[:href]).with_content(item[:label]) %>
5
+ <%= render Uchi::Flowbite::BreadcrumbItem::First.new(href: item[:href]).with_content(item[:label]) %>
6
6
  <% elsif index == items.size - 1 %>
7
- <%= render Flowbite::BreadcrumbItem::Current.new.with_content(item[:label]) %>
7
+ <%= render Uchi::Flowbite::BreadcrumbItem::Current.new.with_content(item[:label]) %>
8
8
  <% else %>
9
- <%= render Flowbite::BreadcrumbItem.new(href: item[:href]).with_content(item[:label]) %>
9
+ <%= render Uchi::Flowbite::BreadcrumbItem.new(href: item[:href]).with_content(item[:label]) %>
10
10
  <% end %>
11
11
  <% end %>
12
12
  <% end %>
@@ -15,6 +15,7 @@
15
15
  <div class="flex items-center h-5">
16
16
  <%= b.check_box(class: checkbox_classes.join(" "), disabled: disabled?) %>
17
17
  </div>
18
+
18
19
  <div class="ms-2 text-sm">
19
20
  <%= b.label(class: checkbox_label_classes.join(" ")) %>
20
21
  </div>
@@ -27,6 +28,6 @@
27
28
  <% end %>
28
29
 
29
30
  <% errors.each do |error| %>
30
- <%= render(Flowbite::Input::ValidationError.new) { error.upcase_first } %>
31
+ <%= render(Uchi::Flowbite::Input::ValidationError.new) { error.upcase_first } %>
31
32
  <% end %>
32
33
  </div>
@@ -68,22 +68,22 @@ module Uchi
68
68
  end
69
69
 
70
70
  def label_classes
71
- base = ["block", "mb-2", "text-sm", "font-medium"]
71
+ base = ["select-none ms-2 text-sm font-medium"]
72
72
  if disabled?
73
- base + ["text-gray-400", "dark:text-gray-500"]
73
+ base + ["text-fg-disabled"]
74
74
  elsif errors?
75
- base + ["text-red-700", "dark:text-red-500"]
75
+ base + ["text-fg-danger-strong"]
76
76
  else
77
- base + ["text-gray-900", "dark:text-white"]
77
+ base + ["text-heading"]
78
78
  end
79
79
  end
80
80
 
81
81
  def hint_classes
82
82
  base = ["text-xs", "font-normal", "mt-1"]
83
83
  if disabled?
84
- base + ["text-gray-400", "dark:text-gray-500"]
84
+ base + ["text-fg-disabled"]
85
85
  else
86
- base + ["text-gray-500", "dark:text-gray-300"]
86
+ base + ["text-body"]
87
87
  end
88
88
  end
89
89
 
@@ -92,26 +92,21 @@ module Uchi
92
92
  end
93
93
 
94
94
  def checkbox_classes
95
- base = ["w-4", "h-4", "rounded-sm", "focus:ring-2", "focus:ring-offset-2"]
95
+ base = ["w-4", "h-4", "border", "rounded-xs", "focus:ring-2"]
96
96
  if disabled?
97
- base + ["text-blue-600", "bg-gray-100", "border-gray-300",
98
- "dark:bg-gray-700", "dark:border-gray-600"]
97
+ base + ["border-light", "bg-neutral-secondary-medium", "focus:ring-brand-soft"]
99
98
  elsif errors?
100
- base + ["text-red-600", "bg-red-50", "border-red-500",
101
- "focus:ring-red-500", "dark:focus:ring-red-600",
102
- "dark:bg-gray-700", "dark:border-red-500"]
99
+ base + ["text-fg-danger", "bg-danger-soft", "border-fg-danger", "focus:ring-fg-danger"]
103
100
  else
104
- base + ["text-blue-600", "bg-gray-100", "border-gray-300",
105
- "focus:ring-blue-500", "dark:focus:ring-blue-600",
106
- "dark:bg-gray-700", "dark:border-gray-600"]
101
+ base + ["border-default-medium", "bg-neutral-secondary-medium", "focus:ring-brand-soft"]
107
102
  end
108
103
  end
109
104
 
110
105
  def checkbox_label_classes
111
106
  if disabled?
112
- ["ms-2", "text-sm", "font-medium", "text-gray-400", "dark:text-gray-500"]
107
+ ["select-none", "ms-2", "text-sm", "font-medium", "text-fg-disabled"]
113
108
  else
114
- ["ms-2", "text-sm", "font-medium", "text-gray-900", "dark:text-gray-300"]
109
+ ["select-none", "ms-2", "text-sm", "font-medium", "text-heading"]
115
110
  end
116
111
  end
117
112
 
@@ -1,3 +1,8 @@
1
- <div class="bg-white border border-gray-200 shadow-sm md:rounded-lg dark:bg-gray-800 dark:border-gray-700">
1
+ <div
2
+ class="
3
+ relative overflow-x-auto bg-neutral-primary-soft shadow-xs rounded-base
4
+ border border-default
5
+ "
6
+ >
2
7
  <%= content %>
3
8
  </div>
@@ -1,31 +1,86 @@
1
- <table class="w-full text-left text-gray-500 rtl:text-right dark:text-gray-400">
2
- <thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
1
+ <%# Styles from https://flowbite.com/docs/components/tables/#default-table %>
2
+ <table class="w-full text-sm text-left text-body rtl:text-right">
3
+ <thead
4
+ class="
5
+ text-sm text-body bg-neutral-secondary-soft border-b rounded-base
6
+ border-default
7
+ "
8
+ >
3
9
  <tr>
4
10
  <% columns.each do |field| %>
5
11
  <% component_class = field.index_component_class %>
6
- <th scope="col" class="px-4 py-3 md:px-6 whitespace-nowrap <%= component_class.classes_for_table_cell.join(" ") %>">
12
+
13
+ <th
14
+ scope="col"
15
+ class="px-6 py-3 font-medium whitespace-nowrap <%= component_class.classes_for_table_cell.join(" ") %>"
16
+ >
7
17
  <% if field.sortable? %>
8
18
  <% if field.name == sort_order&.name %>
9
19
  <% if sort_order&.ascending? %>
10
20
  <%= link_to(repository.routes.path_for(:index, query: query, scope: scope, sort: {:by => field.name, :direction => :desc})) do %>
11
21
  <%= repository.translate.field_label(field) %>
12
- <svg class="inline w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
13
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19V5m0 14-4-4m4 4 4-4"/>
22
+
23
+ <svg
24
+ class="inline w-4 h-4 text-heading"
25
+ aria-hidden="true"
26
+ xmlns="http://www.w3.org/2000/svg"
27
+ width="24"
28
+ height="24"
29
+ fill="none"
30
+ viewBox="0 0 24 24"
31
+ >
32
+ <path
33
+ stroke="currentColor"
34
+ stroke-linecap="round"
35
+ stroke-linejoin="round"
36
+ stroke-width="2"
37
+ d="M12 19V5m0 14-4-4m4 4 4-4"
38
+ />
14
39
  </svg>
15
40
  <% end %>
16
41
  <% else %>
17
42
  <%= link_to(repository.routes.path_for(:index, query: query, scope: scope, sort: {:by => field.name, :direction => :asc})) do %>
18
43
  <%= repository.translate.field_label(field) %>
19
- <svg class="inline w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
20
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v13m0-13 4 4m-4-4-4 4"/>
44
+
45
+ <svg
46
+ class="inline w-4 h-4 text-heading"
47
+ aria-hidden="true"
48
+ xmlns="http://www.w3.org/2000/svg"
49
+ width="24"
50
+ height="24"
51
+ fill="none"
52
+ viewBox="0 0 24 24"
53
+ >
54
+ <path
55
+ stroke="currentColor"
56
+ stroke-linecap="round"
57
+ stroke-linejoin="round"
58
+ stroke-width="2"
59
+ d="M12 6v13m0-13 4 4m-4-4-4 4"
60
+ />
21
61
  </svg>
22
62
  <% end %>
23
63
  <% end %>
24
64
  <% else %>
25
65
  <%= link_to(repository.routes.path_for(:index, query: query, scope: scope, sort: {:by => field.name, :direction => :asc})) do %>
26
66
  <%= repository.translate.field_label(field) %>
27
- <svg class="inline w-4 h-4 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
28
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 20V7m0 13-4-4m4 4 4-4m4-12v13m0-13 4 4m-4-4-4 4"/>
67
+
68
+ <svg
69
+ class="inline w-4 h-4 text-heading"
70
+ aria-hidden="true"
71
+ xmlns="http://www.w3.org/2000/svg"
72
+ width="24"
73
+ height="24"
74
+ fill="none"
75
+ viewBox="0 0 24 24"
76
+ >
77
+ <path
78
+ stroke="currentColor"
79
+ stroke-linecap="round"
80
+ stroke-linejoin="round"
81
+ stroke-width="2"
82
+ d="M8 20V7m0 13-4-4m4 4 4-4m4-12v13m0-13 4 4m-4-4-4 4"
83
+ />
29
84
  </svg>
30
85
  <% end %>
31
86
  <% end %>
@@ -34,29 +89,65 @@
34
89
  <% end %>
35
90
  </th>
36
91
  <% end %>
37
- <th scope="col" class="px-4 py-3 md:px-6"></th>
92
+
93
+ <th scope="col" class="px-6 py-3"></th>
38
94
  </tr>
39
95
  </thead>
96
+
40
97
  <tbody>
41
98
  <% records.each do |record| %>
42
- <tr class="bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
99
+ <tr class="bg-neutral-primary border-b border-default">
43
100
  <% columns.each do |field| %>
44
101
  <% component_class = field.index_component_class %>
45
- <td class="px-4 py-4 md:px-6 <%= component_class.classes_for_table_cell.join(" ") %>">
102
+
103
+ <td class="px-6 py-4 <%= component_class.classes_for_table_cell.join(" ") %>">
46
104
  <%= render(field.index_component(record: record, repository: repository)) %>
47
105
  </td>
48
106
  <% end %>
49
- <td class="px-4 py-4 text-right md:px-6">
107
+
108
+ <td class="px-6 py-4 text-right">
50
109
  <div class="flex justify-end space-x-2">
51
110
  <%= link_to(repository.routes.path_for(:show, id: record.id), class: "inline-block hover:text-blue-600 hover:dark:text-blue-500", :data => {:"turbo-frame" => "_top"}) do %>
52
- <svg class="w-6 h-6" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
53
- <path stroke="currentColor" stroke-width="2" d="M21 12c0 1.2-4.03 6-9 6s-9-4.8-9-6c0-1.2 4.03-6 9-6s9 4.8 9 6Z"/>
54
- <path stroke="currentColor" stroke-width="2" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
111
+ <svg
112
+ class="w-6 h-6"
113
+ aria-hidden="true"
114
+ xmlns="http://www.w3.org/2000/svg"
115
+ width="24"
116
+ height="24"
117
+ fill="none"
118
+ viewBox="0 0 24 24"
119
+ >
120
+ <path
121
+ stroke="currentColor"
122
+ stroke-width="2"
123
+ d="M21 12c0 1.2-4.03 6-9 6s-9-4.8-9-6c0-1.2 4.03-6 9-6s9 4.8 9 6Z"
124
+ />
125
+
126
+ <path
127
+ stroke="currentColor"
128
+ stroke-width="2"
129
+ d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
130
+ />
55
131
  </svg>
56
132
  <% end %>
133
+
57
134
  <%= link_to(path_for_edit(record), class: "inline-block hover:text-blue-600 hover:dark:text-blue-500", :data => {:"turbo-frame" => "_top"}) do %>
58
- <svg class="w-6 h-6" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
59
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m14.304 4.844 2.852 2.852M7 7H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-4.5m2.409-9.91a2.017 2.017 0 0 1 0 2.853l-6.844 6.844L8 14l.713-3.565 6.844-6.844a2.015 2.015 0 0 1 2.852 0Z"/>
135
+ <svg
136
+ class="w-6 h-6"
137
+ aria-hidden="true"
138
+ xmlns="http://www.w3.org/2000/svg"
139
+ width="24"
140
+ height="24"
141
+ fill="none"
142
+ viewBox="0 0 24 24"
143
+ >
144
+ <path
145
+ stroke="currentColor"
146
+ stroke-linecap="round"
147
+ stroke-linejoin="round"
148
+ stroke-width="2"
149
+ d="m14.304 4.844 2.852 2.852M7 7H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h11a1 1 0 0 0 1-1v-4.5m2.409-9.91a2.017 2.017 0 0 1 0 2.853l-6.844 6.844L8 14l.713-3.565 6.844-6.844a2.015 2.015 0 0 1 2.852 0Z"
150
+ />
60
151
  </svg>
61
152
  <% end %>
62
153
  </div>
@@ -1,12 +1,27 @@
1
- <%= form_tag(repository.routes.path_for(:index), class: "flex items-center max-w-sm", method: :get) do %>
1
+ <%= form_tag(repository.routes.path_for(:index), class: "flex items-center max-w-sm space-x-2", method: :get) do %>
2
2
  <%= label_tag(:query, repository.translate.search_label, class: "sr-only") %>
3
+
3
4
  <div class="relative w-full">
4
- <%= search_field_tag(:query, query, class: "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500") %>
5
+ <%= search_field_tag(:query, query, class: "px-3 py-2.5 bg-neutral-secondary-medium border border-default-medium rounded-base text-heading text-sm focus:ring-brand focus:border-brand block w-full placeholder:text-body") %>
5
6
  </div>
6
- <%= button_tag(class: "p-2.5 ms-2 text-sm font-medium text-white bg-blue-700 rounded-lg border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800") do %>
7
- <svg class="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
8
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
7
+
8
+ <%= button_tag(class: "inline-flex items-center justify-center shrink-0 text-white bg-brand hover:bg-brand-strong focus:ring-4 focus:ring-brand-medium shadow-xs rounded-base w-10 h-10 focus:outline-none") do %>
9
+ <svg
10
+ class="w-4 h-4"
11
+ aria-hidden="true"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ fill="none"
14
+ viewBox="0 0 20 20"
15
+ >
16
+ <path
17
+ stroke="currentColor"
18
+ stroke-linecap="round"
19
+ stroke-linejoin="round"
20
+ stroke-width="2"
21
+ d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
22
+ />
9
23
  </svg>
24
+
10
25
  <span class="sr-only"><%= repository.translate.search_button %></span>
11
26
  <% end %>
12
27
 
@@ -16,6 +31,7 @@
16
31
  <%= hidden_field_tag("scope[inverse_of]", scope[:inverse_of]) %>
17
32
  <%= hidden_field_tag("scope[model]", scope[:model]) %>
18
33
  <% end %>
34
+
19
35
  <%= hidden_field_tag("sort[by]", sort_by) if sort_by? %>
20
36
  <%= hidden_field_tag("sort[direction]", sort_direction) if sort_direction? %>
21
37
  <% end %>
@@ -0,0 +1,9 @@
1
+ <ul class="space-y-2 font-medium">
2
+ <% items.each do |item| %>
3
+ <li>
4
+ <%= link_to(item[:path], :class => "flex items-center py-1.5 text-body rounded-base hover:bg-neutral-tertiary hover:text-fg-brand group md:px-2") do %>
5
+ <span class="ms-3"><%= item[:label] %></span>
6
+ <% end %>
7
+ </li>
8
+ <% end %>
9
+ </ul>
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uchi
4
+ module Ui
5
+ # Renders the main navigation menu visible at the side of the viewport.
6
+ #
7
+ # The template for this component has access to the following methods:
8
+ #
9
+ # - `items`: Returns an `Array` of `Hashes` containing :label and :path keys
10
+ # for each item to render in the navigation.
11
+ # - `navigatable_repositories`: Returns an `Array` of `Uchi::Repository`
12
+ # instances. The `Array` is sorted alphanumerically by the
13
+ # `navigation_label` for each repository.
14
+ # - `repositories`: Returns an `Array` with an instance of each
15
+ # `Uchi::Repository` in the application.
16
+ class Navigation < ViewComponent::Base
17
+ protected
18
+
19
+ def items
20
+ navigatable_repositories.map do |repository|
21
+ {
22
+ label: repository.translate.navigation_label,
23
+ path: repository.routes.path_for(:index)
24
+ }
25
+ end
26
+ end
27
+
28
+ def navigatable_repositories
29
+ repositories.sort_by { |repository| repository.translate.navigation_label.downcase }
30
+ end
31
+
32
+ def repositories
33
+ Uchi::Repository.all.map(&:new)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1 @@
1
+ <span class="text-body-subtle">—</span>
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uchi
4
+ module Ui
5
+ class NoValue < ViewComponent::Base
6
+ end
7
+ end
8
+ end
@@ -1,15 +1,20 @@
1
- <header class="px-4 mb-6 space-y-6 md:px-0">
2
- <div>
3
- <% if breadcrumb? %>
1
+ <header class="px-0 mb-6 space-y-6 md:px-0">
2
+ <% if breadcrumb? %>
3
+ <div class="px-2">
4
4
  <%= breadcrumb %>
5
- <% end %>
6
- </div>
5
+ </div>
6
+ <% end %>
7
7
 
8
- <div class="items-start justify-between space-x-6 md:flex md:px-0">
8
+ <div class="items-start justify-between space-y-2 space-x-6 md:flex md:px-0 md:space-y-0">
9
9
  <div>
10
- <h1 class="text-3xl font-semibold tracking-tight text-gray-900 dark:text-white group"><%= title %></h1>
10
+ <h1 class="px-1 text-3xl font-semibold tracking-tight text-heading group">
11
+ <%= title %>
12
+ </h1>
13
+
11
14
  <% if description.present? %>
12
- <div class="text-lg text-gray-500 lg:mb-0 dark:text-gray-400 lg:max-w-2xl"><%= description %></div>
15
+ <div class="text-lg text-body-subtle lg:mb-0 lg:max-w-2xl">
16
+ <%= description %>
17
+ </div>
13
18
  <% end %>
14
19
  </div>
15
20
 
@@ -1,3 +1,3 @@
1
1
  <li>
2
- <%= link_to(@paginator.page, page_url(page_number), aria: { current: "page" }, class: "z-10 flex items-center justify-center px-3 h-8 leading-tight text-blue-600 border border-blue-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white") %>
2
+ <%= link_to(@paginator.page, page_url(page_number), aria: { current: "page" }, class: "flex items-center justify-center text-fg-brand bg-neutral-tertiary-medium box-border border border-default-medium hover:text-fg-brand font-medium text-sm w-9 h-9 focus:outline-none") %>
3
3
  </li>
@@ -1,3 +1,9 @@
1
- <li class="flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400">
1
+ <li
2
+ class="
3
+ flex items-center justify-center text-body bg-neutral-secondary-medium
4
+ box-border border border-default-medium hover:bg-neutral-tertiary-medium
5
+ hover:text-heading font-medium text-sm w-9 h-9 focus:outline-none
6
+ "
7
+ >
2
8
  &hellip;
3
9
  </li>
@@ -1,3 +1,3 @@
1
1
  <li>
2
- <%= link_to(page_number, page_url(page_number), class: "flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white") %>
2
+ <%= link_to(page_number, page_url(page_number), class: "flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium text-sm w-9 h-9 focus:outline-none") %>
3
3
  </li>
@@ -1,8 +1,23 @@
1
1
  <li>
2
- <%= link_to(url, class: "flex items-center justify-center px-3 h-8 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white") do %>
2
+ <%= link_to(url, class: "flex items-center justify-center text-body bg-neutral-secondary-medium box-border border border-default-medium hover:bg-neutral-tertiary-medium hover:text-heading font-medium rounded-e-base text-sm w-9 h-9 focus:outline-none") do %>
3
3
  <span class="sr-only">Next</span>
4
- <svg class="w-3 h-3 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
5
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4"/>
4
+
5
+ <svg
6
+ class="w-4 h-4 rtl:rotate-180"
7
+ aria-hidden="true"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ width="24"
10
+ height="24"
11
+ fill="none"
12
+ viewBox="0 0 24 24"
13
+ >
14
+ <path
15
+ stroke="currentColor"
16
+ stroke-linecap="round"
17
+ stroke-linejoin="round"
18
+ stroke-width="2"
19
+ d="m9 5 7 7-7 7"
20
+ />
6
21
  </svg>
7
22
  <% end %>
8
23
  </li>
@@ -1,6 +1,7 @@
1
1
  <nav aria-label="<%= aria_label %>">
2
- <ul class="flex items-center -space-x-px h-8 text-sm">
2
+ <ul class="flex -space-x-px text-sm">
3
3
  <%= render(Uchi::Ui::Pagination::PreviousLink.new(paginator: @paginator)) %>
4
+
4
5
  <% pages.each do |page_number| %>
5
6
  <% if page_number.nil? %>
6
7
  <%= render(Uchi::Ui::Pagination::Gap.new(page_number: page_number, paginator: @paginator)) %>
@@ -10,6 +11,7 @@
10
11
  <%= render(Uchi::Ui::Pagination::Link.new(page_number: page_number, paginator: @paginator)) %>
11
12
  <% end %>
12
13
  <% end %>
14
+
13
15
  <%= render(Uchi::Ui::Pagination::NextLink.new(paginator: @paginator)) %>
14
16
  </ul>
15
17
  </nav>