uchi 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +222 -0
- data/app/assets/config/uchi_manifest.js +2 -0
- data/app/assets/javascripts/uchi/application.js +6095 -0
- data/app/assets/javascripts/uchi.js +4 -0
- data/app/assets/stylesheets/uchi/application.css +3971 -0
- data/app/assets/stylesheets/uchi/uchi.css +17 -0
- data/app/assets/tailwind/uchi.css +21 -0
- data/app/components/flowbite/breadcrumb.rb +33 -0
- data/app/components/flowbite/breadcrumb_home.rb +26 -0
- data/app/components/flowbite/breadcrumb_item/current.rb +33 -0
- data/app/components/flowbite/breadcrumb_item/first.rb +35 -0
- data/app/components/flowbite/breadcrumb_item.rb +48 -0
- data/app/components/flowbite/breadcrumb_separator.rb +30 -0
- data/app/components/flowbite/button/outline.rb +22 -0
- data/app/components/flowbite/button/pill.rb +40 -0
- data/app/components/flowbite/button.rb +92 -0
- data/app/components/flowbite/card.rb +45 -0
- data/app/components/flowbite/input/checkbox.rb +73 -0
- data/app/components/flowbite/input/date.rb +11 -0
- data/app/components/flowbite/input/date_time.rb +11 -0
- data/app/components/flowbite/input/email.rb +12 -0
- data/app/components/flowbite/input/field.rb +117 -0
- data/app/components/flowbite/input/file.rb +30 -0
- data/app/components/flowbite/input/hint.rb +57 -0
- data/app/components/flowbite/input/label.rb +82 -0
- data/app/components/flowbite/input/number.rb +11 -0
- data/app/components/flowbite/input/password.rb +11 -0
- data/app/components/flowbite/input/phone.rb +11 -0
- data/app/components/flowbite/input/radio_button.rb +50 -0
- data/app/components/flowbite/input/select.rb +49 -0
- data/app/components/flowbite/input/textarea.rb +42 -0
- data/app/components/flowbite/input/url.rb +12 -0
- data/app/components/flowbite/input/validation_error.rb +11 -0
- data/app/components/flowbite/input_field/checkbox.html.erb +14 -0
- data/app/components/flowbite/input_field/checkbox.rb +54 -0
- data/app/components/flowbite/input_field/date.rb +13 -0
- data/app/components/flowbite/input_field/date_time.rb +13 -0
- data/app/components/flowbite/input_field/email.rb +13 -0
- data/app/components/flowbite/input_field/file.rb +13 -0
- data/app/components/flowbite/input_field/input_field.html.erb +8 -0
- data/app/components/flowbite/input_field/number.rb +13 -0
- data/app/components/flowbite/input_field/password.rb +13 -0
- data/app/components/flowbite/input_field/phone.rb +13 -0
- data/app/components/flowbite/input_field/radio_button.html.erb +14 -0
- data/app/components/flowbite/input_field/radio_button.rb +86 -0
- data/app/components/flowbite/input_field/select.rb +31 -0
- data/app/components/flowbite/input_field/text.rb +8 -0
- data/app/components/flowbite/input_field/textarea.rb +13 -0
- data/app/components/flowbite/input_field/url.rb +13 -0
- data/app/components/flowbite/input_field.rb +192 -0
- data/app/components/flowbite/link.rb +21 -0
- data/app/components/flowbite/style.rb +13 -0
- data/app/components/flowbite/toast/icon.html.erb +5 -0
- data/app/components/flowbite/toast/icon.rb +57 -0
- data/app/components/flowbite/toast/toast.html.erb +11 -0
- data/app/components/flowbite/toast.rb +34 -0
- data/app/components/uchi/field/base.rb +57 -0
- data/app/components/uchi/field/belongs_to/edit.html.erb +1 -0
- data/app/components/uchi/field/belongs_to/index.html.erb +1 -0
- data/app/components/uchi/field/belongs_to/show.html.erb +3 -0
- data/app/components/uchi/field/belongs_to.rb +105 -0
- data/app/components/uchi/field/blank/edit.html.erb +1 -0
- data/app/components/uchi/field/blank/index.html.erb +1 -0
- data/app/components/uchi/field/blank/show.html.erb +1 -0
- data/app/components/uchi/field/blank.rb +16 -0
- data/app/components/uchi/field/boolean/edit.html.erb +1 -0
- data/app/components/uchi/field/boolean/index.html.erb +9 -0
- data/app/components/uchi/field/boolean/show.html.erb +9 -0
- data/app/components/uchi/field/boolean.rb +27 -0
- data/app/components/uchi/field/date/edit.html.erb +1 -0
- data/app/components/uchi/field/date/index.html.erb +1 -0
- data/app/components/uchi/field/date/show.html.erb +1 -0
- data/app/components/uchi/field/date.rb +27 -0
- data/app/components/uchi/field/date_time/edit.html.erb +1 -0
- data/app/components/uchi/field/date_time/index.html.erb +1 -0
- data/app/components/uchi/field/date_time/show.html.erb +1 -0
- data/app/components/uchi/field/date_time.rb +27 -0
- data/app/components/uchi/field/file/edit.html.erb +1 -0
- data/app/components/uchi/field/file/index.html.erb +6 -0
- data/app/components/uchi/field/file/show.html.erb +8 -0
- data/app/components/uchi/field/file.rb +37 -0
- data/app/components/uchi/field/has_and_belongs_to_many/edit.html.erb +9 -0
- data/app/components/uchi/field/has_and_belongs_to_many/index.html.erb +1 -0
- data/app/components/uchi/field/has_and_belongs_to_many/show.html.erb +28 -0
- data/app/components/uchi/field/has_and_belongs_to_many.rb +131 -0
- data/app/components/uchi/field/has_many/edit.html.erb +1 -0
- data/app/components/uchi/field/has_many/index.html.erb +1 -0
- data/app/components/uchi/field/has_many/show.html.erb +28 -0
- data/app/components/uchi/field/has_many.rb +107 -0
- data/app/components/uchi/field/id/index.html.erb +4 -0
- data/app/components/uchi/field/id/show.html.erb +4 -0
- data/app/components/uchi/field/id.rb +26 -0
- data/app/components/uchi/field/image/edit.html.erb +1 -0
- data/app/components/uchi/field/image/index.html.erb +6 -0
- data/app/components/uchi/field/image/show.html.erb +6 -0
- data/app/components/uchi/field/image.rb +38 -0
- data/app/components/uchi/field/number/edit.html.erb +1 -0
- data/app/components/uchi/field/number/index.html.erb +1 -0
- data/app/components/uchi/field/number/show.html.erb +1 -0
- data/app/components/uchi/field/number.rb +32 -0
- data/app/components/uchi/field/string/edit.html.erb +1 -0
- data/app/components/uchi/field/string/index.html.erb +1 -0
- data/app/components/uchi/field/string/show.html.erb +1 -0
- data/app/components/uchi/field/string.rb +33 -0
- data/app/components/uchi/field/text/edit.html.erb +1 -0
- data/app/components/uchi/field/text/index.html.erb +1 -0
- data/app/components/uchi/field/text/show.html.erb +1 -0
- data/app/components/uchi/field/text.rb +38 -0
- data/app/components/uchi/ui/breadcrumb/breadcrumb.html.erb +13 -0
- data/app/components/uchi/ui/breadcrumb.rb +14 -0
- data/app/components/uchi/ui/form/footer/footer.html.erb +5 -0
- data/app/components/uchi/ui/form/footer.rb +15 -0
- data/app/components/uchi/ui/form/input/collection_checkboxes.html.erb +32 -0
- data/app/components/uchi/ui/form/input/collection_checkboxes.rb +125 -0
- data/app/components/uchi/ui/frame/frame.html.erb +3 -0
- data/app/components/uchi/ui/frame.rb +10 -0
- data/app/components/uchi/ui/index/records_table/records_table.html.erb +67 -0
- data/app/components/uchi/ui/index/records_table/search_form/search_form.html.erb +21 -0
- data/app/components/uchi/ui/index/records_table/search_form.rb +49 -0
- data/app/components/uchi/ui/index/records_table.rb +29 -0
- data/app/components/uchi/ui/index/turbo_frame.rb +50 -0
- data/app/components/uchi/ui/page_header/page_header.html.erb +24 -0
- data/app/components/uchi/ui/page_header.rb +18 -0
- data/app/components/uchi/ui/pagination/current_link.html.erb +3 -0
- data/app/components/uchi/ui/pagination/current_link.rb +10 -0
- data/app/components/uchi/ui/pagination/gap.html.erb +3 -0
- data/app/components/uchi/ui/pagination/gap.rb +10 -0
- data/app/components/uchi/ui/pagination/item.rb +24 -0
- data/app/components/uchi/ui/pagination/link.html.erb +3 -0
- data/app/components/uchi/ui/pagination/link.rb +10 -0
- data/app/components/uchi/ui/pagination/next_link.html.erb +8 -0
- data/app/components/uchi/ui/pagination/next_link.rb +22 -0
- data/app/components/uchi/ui/pagination/pagination.html.erb +15 -0
- data/app/components/uchi/ui/pagination/previous_link.html.erb +8 -0
- data/app/components/uchi/ui/pagination/previous_link.rb +22 -0
- data/app/components/uchi/ui/pagination.rb +48 -0
- data/app/components/uchi/ui/show/attribute_fields/attribute_fields.html.erb +14 -0
- data/app/components/uchi/ui/show/attribute_fields.rb +18 -0
- data/app/components/uchi/ui/spinner/spinner.html.erb +7 -0
- data/app/components/uchi/ui/spinner.rb +15 -0
- data/app/controllers/uchi/application_controller.rb +4 -0
- data/app/controllers/uchi/controller.rb +13 -0
- data/app/controllers/uchi/repository_controller.rb +166 -0
- data/app/helpers/uchi/application_helper.rb +17 -0
- data/app/jobs/uchi/application_job.rb +4 -0
- data/app/mailers/uchi/application_mailer.rb +6 -0
- data/app/views/layouts/uchi/_flash_messages.html.erb +10 -0
- data/app/views/layouts/uchi/application.html.erb +33 -0
- data/app/views/uchi/repository/edit.html.erb +40 -0
- data/app/views/uchi/repository/index.html.erb +49 -0
- data/app/views/uchi/repository/new.html.erb +37 -0
- data/app/views/uchi/repository/show.html.erb +41 -0
- data/lib/generators/uchi/controller/controller_generator.rb +16 -0
- data/lib/generators/uchi/controller/templates/controller.rb.tt +11 -0
- data/lib/generators/uchi/install/install_generator.rb +13 -0
- data/lib/generators/uchi/repository/repository_generator.rb +16 -0
- data/lib/generators/uchi/repository/templates/repository.rb.tt +11 -0
- data/lib/tasks/uchi_tasks.rake +4 -0
- data/lib/uchi/application_record.rb +5 -0
- data/lib/uchi/engine.rb +24 -0
- data/lib/uchi/field/configuration.rb +142 -0
- data/lib/uchi/field.rb +80 -0
- data/lib/uchi/i18n.rb +13 -0
- data/lib/uchi/pagination/controller.rb +26 -0
- data/lib/uchi/pagination/page.rb +20 -0
- data/lib/uchi/pagy/LICENSE.txt +21 -0
- data/lib/uchi/pagy/classes/exceptions.rb +35 -0
- data/lib/uchi/pagy/classes/offset/offset.rb +56 -0
- data/lib/uchi/pagy/classes/request.rb +38 -0
- data/lib/uchi/pagy/modules/abilities/configurable.rb +38 -0
- data/lib/uchi/pagy/modules/abilities/linkable.rb +62 -0
- data/lib/uchi/pagy/modules/abilities/rangeable.rb +17 -0
- data/lib/uchi/pagy/modules/abilities/shiftable.rb +14 -0
- data/lib/uchi/pagy/modules/console.rb +40 -0
- data/lib/uchi/pagy/toolbox/helpers/loader.rb +19 -0
- data/lib/uchi/pagy/toolbox/helpers/page_url.rb +25 -0
- data/lib/uchi/pagy/toolbox/paginators/method.rb +21 -0
- data/lib/uchi/pagy/toolbox/paginators/offset.rb +35 -0
- data/lib/uchi/pagy.rb +60 -0
- data/lib/uchi/repository/routes.rb +62 -0
- data/lib/uchi/repository/translate.rb +284 -0
- data/lib/uchi/repository.rb +156 -0
- data/lib/uchi/sort_order.rb +35 -0
- data/lib/uchi/version.rb +5 -0
- data/lib/uchi.rb +18 -0
- data/uchi.gemspec +35 -0
- metadata +197 -107
- data/.github/dependabot.yml +0 -17
- data/.github/workflows/build.yml +0 -23
- data/.github/workflows/lint.yml +0 -30
- data/package.json +0 -31
- data/sig/uchi.rbs +0 -4
- data/test/components/uchi/field/belongs_to_test.rb +0 -134
- data/test/components/uchi/field/blank_test.rb +0 -119
- data/test/components/uchi/field/boolean_test.rb +0 -163
- data/test/components/uchi/field/date_test.rb +0 -163
- data/test/components/uchi/field/date_time_test.rb +0 -152
- data/test/components/uchi/field/has_many_test.rb +0 -138
- data/test/components/uchi/field/id_test.rb +0 -113
- data/test/components/uchi/field/number_test.rb +0 -163
- data/test/components/uchi/field/string_test.rb +0 -159
- data/test/controllers/uchi/authors_controller_test.rb +0 -119
- data/test/controllers/uchi/repository_controller_test.rb +0 -93
- data/test/controllers/uchi/scoped_repository_controller_test.rb +0 -73
- data/test/dummy/Rakefile +0 -6
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +0 -15
- data/test/dummy/app/controllers/application_controller.rb +0 -4
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/uchi/authors_controller.rb +0 -7
- data/test/dummy/app/controllers/uchi/books_controller.rb +0 -7
- data/test/dummy/app/controllers/uchi/titles_controller.rb +0 -7
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/jobs/application_job.rb +0 -7
- data/test/dummy/app/mailers/application_mailer.rb +0 -4
- data/test/dummy/app/models/application_record.rb +0 -3
- data/test/dummy/app/models/author.rb +0 -3
- data/test/dummy/app/models/book.rb +0 -3
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/title.rb +0 -3
- data/test/dummy/app/uchi/repositories/author.rb +0 -20
- data/test/dummy/app/uchi/repositories/book.rb +0 -16
- data/test/dummy/app/uchi/repositories/title.rb +0 -17
- data/test/dummy/app/views/layouts/application.html.erb +0 -27
- data/test/dummy/app/views/layouts/mailer.html.erb +0 -13
- data/test/dummy/app/views/layouts/mailer.text.erb +0 -1
- data/test/dummy/app/views/pwa/manifest.json.erb +0 -22
- data/test/dummy/app/views/pwa/service-worker.js +0 -26
- data/test/dummy/bin/dev +0 -2
- data/test/dummy/bin/rails +0 -4
- data/test/dummy/bin/rake +0 -4
- data/test/dummy/bin/setup +0 -34
- data/test/dummy/config/application.rb +0 -29
- data/test/dummy/config/boot.rb +0 -5
- data/test/dummy/config/cable.yml +0 -10
- data/test/dummy/config/database.yml +0 -32
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -69
- data/test/dummy/config/environments/production.rb +0 -89
- data/test/dummy/config/environments/test.rb +0 -53
- data/test/dummy/config/initializers/content_security_policy.rb +0 -25
- data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -8
- data/test/dummy/config/initializers/inflections.rb +0 -16
- data/test/dummy/config/locales/da.yml +0 -51
- data/test/dummy/config/locales/en.yml +0 -31
- data/test/dummy/config/puma.rb +0 -38
- data/test/dummy/config/routes.rb +0 -9
- data/test/dummy/config/storage.yml +0 -34
- data/test/dummy/config.ru +0 -6
- data/test/dummy/db/migrate/20251002183635_create_authors.rb +0 -11
- data/test/dummy/db/migrate/20251005131726_create_books.rb +0 -9
- data/test/dummy/db/migrate/20251005131811_create_titles.rb +0 -11
- data/test/dummy/db/schema.rb +0 -38
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/400.html +0 -114
- data/test/dummy/public/404.html +0 -114
- data/test/dummy/public/406-unsupported-browser.html +0 -114
- data/test/dummy/public/422.html +0 -114
- data/test/dummy/public/500.html +0 -114
- data/test/dummy/public/icon.png +0 -0
- data/test/dummy/public/icon.svg +0 -3
- data/test/dummy/storage/.keep +0 -0
- data/test/dummy/test/fixtures/authors.yml +0 -11
- data/test/dummy/test/models/author_test.rb +0 -7
- data/test/dummy/tmp/.keep +0 -0
- data/test/dummy/tmp/pids/.keep +0 -0
- data/test/dummy/tmp/storage/.keep +0 -0
- data/test/test_helper.rb +0 -15
- data/test/uchi/field_test.rb +0 -63
- data/test/uchi/i18n_test.rb +0 -18
- data/test/uchi/repository/routes_test.rb +0 -49
- data/test/uchi/repository/translate_test.rb +0 -263
- data/test/uchi/repository_test.rb +0 -137
- data/test/uchi/sort_order_test.rb +0 -47
- data/test/uchi_test.rb +0 -7
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* Uchi UI */
|
|
2
|
+
|
|
3
|
+
/* This file is compiled into app/assets/builds/uchi.css by
|
|
4
|
+
tailwindcss running tailwindcss --watch in Procfile.dev */
|
|
5
|
+
|
|
6
|
+
/* We need tailwindcss, duh */
|
|
7
|
+
@import "tailwindcss";
|
|
8
|
+
|
|
9
|
+
/* Import the default theme variables from Flowbite
|
|
10
|
+
This imports them from node_modules/flowbite/src/themes/default.css */
|
|
11
|
+
@import "flowbite/src/themes/default";
|
|
12
|
+
|
|
13
|
+
/* Import the Flowbite plugin */
|
|
14
|
+
@plugin "flowbite/plugin";
|
|
15
|
+
|
|
16
|
+
/* Configure the source files of Flowbite */
|
|
17
|
+
@source "../../../node_modules/flowbite";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* Uchi UI */
|
|
2
|
+
|
|
3
|
+
/* This file is compiled into app/assets/stylesheets/uchi/application.css by
|
|
4
|
+
running the following command:
|
|
5
|
+
|
|
6
|
+
tailwindcss -i app/assets/tailwind/uchi.css -o app/assets/stylesheets/uchi/application.css
|
|
7
|
+
npx @tailwindcss/cli -i ./app/assets/tailwind/uchi.css -o ./app/assets/stylesheets/uchi/application.css
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* We need tailwindcss, duh */
|
|
11
|
+
@import "tailwindcss";
|
|
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
|
+
/* Import the Flowbite plugin */
|
|
18
|
+
@plugin "flowbite/plugin";
|
|
19
|
+
|
|
20
|
+
/* Configure the source files of Flowbite */
|
|
21
|
+
@source "../../../node_modules/flowbite";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders a breadcrumb navigation component.
|
|
5
|
+
#
|
|
6
|
+
# See https://flowbite.com/docs/components/breadcrumb/
|
|
7
|
+
#
|
|
8
|
+
# @example Basic usage with BreadcrumbItem components
|
|
9
|
+
# <%= render Flowbite::Breadcrumb.new do |breadcrumb| %>
|
|
10
|
+
# <% breadcrumb.with_item do %>
|
|
11
|
+
# <%= render Flowbite::BreadcrumbItem::First.new(href: "/") { "Home" } %>
|
|
12
|
+
# <% end %>
|
|
13
|
+
# <% breadcrumb.with_item do %>
|
|
14
|
+
# <%= render Flowbite::BreadcrumbItem.new(href: "/projects") { "Projects" } %>
|
|
15
|
+
# <% end %>
|
|
16
|
+
# <% breadcrumb.with_item do %>
|
|
17
|
+
# <%= render Flowbite::BreadcrumbItem::Current.new { "Current Page" } %>
|
|
18
|
+
# <% end %>
|
|
19
|
+
# <% end %>
|
|
20
|
+
class Breadcrumb < ViewComponent::Base
|
|
21
|
+
renders_many :items
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
content_tag(:nav, class: "flex", "aria-label": "Breadcrumb") do
|
|
25
|
+
content_tag(:ol, class: "inline-flex items-center space-x-1 md:space-x-2 rtl:space-x-reverse") do
|
|
26
|
+
items.each do |item|
|
|
27
|
+
concat(item)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders a breadcrumb home icon.
|
|
5
|
+
#
|
|
6
|
+
# This is typically used as a prefix icon in the first breadcrumb item,
|
|
7
|
+
# but can be used standalone if needed.
|
|
8
|
+
#
|
|
9
|
+
# @example Standalone usage
|
|
10
|
+
# <%= render Flowbite::BreadcrumbHome.new %>
|
|
11
|
+
class BreadcrumbHome < ViewComponent::Base
|
|
12
|
+
def call
|
|
13
|
+
tag.svg(
|
|
14
|
+
class: "w-3 h-3 me-2.5",
|
|
15
|
+
"aria-hidden": "true",
|
|
16
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
17
|
+
fill: "currentColor",
|
|
18
|
+
viewBox: "0 0 20 20"
|
|
19
|
+
) do
|
|
20
|
+
tag.path(
|
|
21
|
+
d: "m19.707 9.293-2-2-7-7a1 1 0 0 0-1.414 0l-7 7-2 2a1 1 0 0 0 1.414 1.414L2 10.414V18a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 0 1 1h3a2 2 0 0 0 2-2v-7.586l.293.293a1 1 0 0 0 1.414-1.414Z"
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class BreadcrumbItem
|
|
5
|
+
# Renders the current page breadcrumb item.
|
|
6
|
+
# Current items are rendered as non-interactive spans with different styling.
|
|
7
|
+
#
|
|
8
|
+
# @param options [Hash] Additional HTML attributes to pass to the span element.
|
|
9
|
+
#
|
|
10
|
+
# @example Current page item
|
|
11
|
+
# <%= render Flowbite::BreadcrumbItem::Current.new { "Current Page" } %>
|
|
12
|
+
class Current < BreadcrumbItem
|
|
13
|
+
def initialize(**options)
|
|
14
|
+
super(href: nil, **options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
def item_options
|
|
20
|
+
{"aria-current": "page"}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render_link
|
|
24
|
+
link_options = {class: link_classes}.merge(options)
|
|
25
|
+
content_tag(:span, content, **link_options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def link_classes
|
|
29
|
+
["ms-1", "text-sm", "font-medium", "text-gray-500", "dark:text-gray-400"]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class BreadcrumbItem
|
|
5
|
+
# Renders the first breadcrumb item (typically home).
|
|
6
|
+
# First items don't show a separator icon.
|
|
7
|
+
#
|
|
8
|
+
# @param href [String] The URL for the breadcrumb link.
|
|
9
|
+
# @param options [Hash] Additional HTML attributes to pass to the link element.
|
|
10
|
+
#
|
|
11
|
+
# @example First item
|
|
12
|
+
# <%= render Flowbite::BreadcrumbItem::First.new(href: "/") { "Home" } %>
|
|
13
|
+
class First < BreadcrumbItem
|
|
14
|
+
protected
|
|
15
|
+
|
|
16
|
+
def item_options
|
|
17
|
+
{class: "inline-flex items-center"}
|
|
18
|
+
end
|
|
19
|
+
|
|
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"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def prefix_icon
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render_link
|
|
29
|
+
icon = render(Flowbite::BreadcrumbHome.new)
|
|
30
|
+
link_options = {class: link_classes}.merge(options)
|
|
31
|
+
content_tag(:a, safe_join([icon, content]), href: href, **link_options)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Base class for rendering a breadcrumb item (middle items in the breadcrumb trail).
|
|
5
|
+
#
|
|
6
|
+
# @param href [String] The URL for the breadcrumb link.
|
|
7
|
+
# @param options [Hash] Additional HTML attributes to pass to the link element.
|
|
8
|
+
#
|
|
9
|
+
# @example Middle item
|
|
10
|
+
# <%= render Flowbite::BreadcrumbItem.new(href: "/projects") { "Projects" } %>
|
|
11
|
+
class BreadcrumbItem < ViewComponent::Base
|
|
12
|
+
attr_reader :href, :options
|
|
13
|
+
|
|
14
|
+
def initialize(href:, **options)
|
|
15
|
+
super()
|
|
16
|
+
@href = href
|
|
17
|
+
@options = options
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
content_tag(:li, item_options) do
|
|
22
|
+
content_tag(:div, class: "flex items-center") do
|
|
23
|
+
concat(render(prefix_icon)) if prefix_icon
|
|
24
|
+
concat(render_link)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
protected
|
|
30
|
+
|
|
31
|
+
def item_options
|
|
32
|
+
{}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def prefix_icon
|
|
36
|
+
Flowbite::BreadcrumbSeparator.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def render_link
|
|
40
|
+
link_options = {class: link_classes}.merge(options)
|
|
41
|
+
content_tag(:a, content, href: href, **link_options)
|
|
42
|
+
end
|
|
43
|
+
|
|
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"]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders a breadcrumb separator icon.
|
|
5
|
+
#
|
|
6
|
+
# This is automatically used by BreadcrumbItem components, but can be
|
|
7
|
+
# used standalone if needed.
|
|
8
|
+
#
|
|
9
|
+
# @example Standalone usage
|
|
10
|
+
# <%= render Flowbite::BreadcrumbSeparator.new %>
|
|
11
|
+
class BreadcrumbSeparator < ViewComponent::Base
|
|
12
|
+
def call
|
|
13
|
+
tag.svg(
|
|
14
|
+
class: "rtl:rotate-180 w-3 h-3 text-gray-400 mx-1",
|
|
15
|
+
"aria-hidden": "true",
|
|
16
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
17
|
+
fill: "none",
|
|
18
|
+
viewBox: "0 0 6 10"
|
|
19
|
+
) do
|
|
20
|
+
tag.path(
|
|
21
|
+
stroke: "currentColor",
|
|
22
|
+
"stroke-linecap": "round",
|
|
23
|
+
"stroke-linejoin": "round",
|
|
24
|
+
"stroke-width": "2",
|
|
25
|
+
d: "m1 9 4-4-4-4"
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Button
|
|
5
|
+
class Outline < Flowbite::Button
|
|
6
|
+
class << self
|
|
7
|
+
# rubocop:disable Layout/LineLength
|
|
8
|
+
def styles
|
|
9
|
+
{
|
|
10
|
+
default: Flowbite::Style.new(
|
|
11
|
+
default: ["text-blue-700", "hover:text-white", "border", "border-blue-700", "hover:bg-blue-800", "focus:ring-4", "focus:outline-none", "focus:ring-blue-300", "font-medium", "rounded-lg", "text-center", "me-2", "mb-2", "dark:border-blue-500", "dark:text-blue-500", "dark:hover:text-white", "dark:hover:bg-blue-500", "dark:focus:ring-blue-800"]
|
|
12
|
+
),
|
|
13
|
+
green: Flowbite::Style.new(
|
|
14
|
+
default: ["text-green-700", "hover:text-white", "border", "border-green-700", "hover:bg-green-800", "focus:ring-4", "focus:outline-none", "focus:ring-green-300", "font-medium", "rounded-lg", "text-center", "me-2", "mb-2", "dark:border-green-500", "dark:text-green-500", "dark:hover:text-white", "dark:hover:bg-green-600", "dark:focus:ring-green-800"]
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
# rubocop:enable Layout/LineLength
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
class Button
|
|
5
|
+
class Pill < Flowbite::Button
|
|
6
|
+
class << self
|
|
7
|
+
# rubocop:disable Layout/LineLength, Metrics/MethodLength
|
|
8
|
+
def styles
|
|
9
|
+
{
|
|
10
|
+
alternative: Flowbite::Style.new(
|
|
11
|
+
default: ["text-sm", "font-medium", "text-gray-900", "focus:outline-none", "bg-white", "rounded-full", "border", "border-gray-200", "hover:bg-gray-100", "hover:text-blue-700", "focus:z-10", "focus:ring-4", "focus:ring-gray-100", "dark:focus:ring-gray-700", "dark:bg-gray-800", "dark:text-gray-400", "dark:border-gray-600", "dark:hover:text-white", "dark:hover:bg-gray-700"]
|
|
12
|
+
),
|
|
13
|
+
dark: Flowbite::Style.new(
|
|
14
|
+
default: ["text-white", "bg-gray-800", "hover:bg-gray-900", "focus:outline-none", "focus:ring-4", "focus:ring-gray-300", "font-medium", "rounded-full", "dark:bg-gray-800", "dark:hover:bg-gray-700", "dark:focus:ring-gray-700", "dark:border-gray-700"]
|
|
15
|
+
),
|
|
16
|
+
default: Flowbite::Style.new(
|
|
17
|
+
default: ["text-white", "bg-blue-700", "hover:bg-blue-800", "focus:outline-none", "focus:ring-4", "focus:ring-blue-300", "font-medium", "rounded-full", "text-center", "dark:bg-blue-600", "dark:hover:bg-blue-700", "dark:focus:ring-blue-800"]
|
|
18
|
+
),
|
|
19
|
+
green: Flowbite::Style.new(
|
|
20
|
+
default: ["text-white", "bg-green-700", "hover:bg-green-800", "focus:outline-none", "focus:ring-4", "focus:ring-green-300", "font-medium", "rounded-full", "text-center", "dark:bg-green-600", "dark:hover:bg-green-700", "dark:focus:ring-green-800"]
|
|
21
|
+
),
|
|
22
|
+
light: Flowbite::Style.new(
|
|
23
|
+
default: ["text-gray-900", "bg-white", "border", "border-gray-300", "focus:outline-none", "hover:bg-gray-100", "focus:ring-4", "focus:ring-gray-100", "font-medium", "rounded-full", "dark:bg-gray-800", "dark:text-white", "dark:border-gray-600", "dark:hover:bg-gray-700", "dark:hover:border-gray-600", "dark:focus:ring-gray-700"]
|
|
24
|
+
),
|
|
25
|
+
purple: Flowbite::Style.new(
|
|
26
|
+
default: ["text-white", "bg-purple-700", "hover:bg-purple-800", "focus:outline-none", "focus:ring-4", "focus:ring-purple-300", "font-medium", "rounded-full", "text-center", "dark:bg-purple-600", "dark:hover:bg-purple-700", "dark:focus:ring-purple-900"]
|
|
27
|
+
),
|
|
28
|
+
red: Flowbite::Style.new(
|
|
29
|
+
default: ["text-white", "bg-red-700", "hover:bg-red-800", "focus:outline-none", "focus:ring-4", "focus:ring-red-300", "font-medium", "rounded-full", "text-center", "dark:bg-red-600", "dark:hover:bg-red-700", "dark:focus:ring-red-900"]
|
|
30
|
+
),
|
|
31
|
+
yellow: Flowbite::Style.new(
|
|
32
|
+
default: ["text-white", "bg-yellow-400", "hover:bg-yellow-500", "focus:outline-none", "focus:ring-4", "focus:ring-yellow-300", "font-medium", "rounded-full", "text-center", "dark:focus:ring-yellow-900"]
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
# rubocop:enable Layout/LineLength, Metrics/MethodLength
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module 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-2"],
|
|
15
|
+
sm: ["text-sm", "px-3", "py-2"],
|
|
16
|
+
default: ["text-sm", "px-5", "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)
|
|
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
|
+
alternative: Flowbite::Style.new(
|
|
36
|
+
default: ["font-medium", "text-gray-900", "focus:outline-none", "bg-white", "rounded-lg", "border", "border-gray-200", "hover:bg-gray-100", "hover:text-blue-700", "focus:z-10", "focus:ring-4", "focus:ring-gray-100", "dark:focus:ring-gray-700", "dark:bg-gray-800", "dark:text-gray-400", "dark:border-gray-600", "dark:hover:text-white", "dark:hover:bg-gray-700"]
|
|
37
|
+
),
|
|
38
|
+
dark: Flowbite::Style.new(
|
|
39
|
+
default: ["text-white", "bg-gray-800", "hover:bg-gray-900", "focus:ring-4", "focus:ring-gray-300", "font-medium", "rounded-lg", "dark:bg-gray-800", "dark:hover:bg-gray-700", "dark:focus:ring-gray-700", "dark:border-gray-700"]
|
|
40
|
+
),
|
|
41
|
+
default: Flowbite::Style.new(
|
|
42
|
+
default: ["text-white", "bg-blue-700", "hover:bg-blue-800", "focus:ring-4", "focus:ring-blue-300", "font-medium", "rounded-lg", "dark:bg-blue-600", "dark:hover:bg-blue-700", "focus:outline-none", "dark:focus:ring-blue-800"]
|
|
43
|
+
),
|
|
44
|
+
green: Flowbite::Style.new(
|
|
45
|
+
default: ["focus:outline-none", "text-white", "bg-green-700", "hover:bg-green-800", "focus:ring-4", "focus:ring-green-300", "font-medium", "rounded-lg", "dark:bg-green-600", "dark:hover:bg-green-700", "dark:focus:ring-green-800"]
|
|
46
|
+
),
|
|
47
|
+
light: Flowbite::Style.new(
|
|
48
|
+
default: ["text-gray-900", "bg-white", "border", "border-gray-300", "hover:bg-gray-100", "focus:ring-4", "focus:ring-gray-100", "font-medium", "rounded-lg", "dark:bg-gray-800", "dark:text-white", "dark:border-gray-600", "dark:hover:bg-gray-700", "dark:hover:border-gray-600", "dark:focus:ring-gray-700"]
|
|
49
|
+
),
|
|
50
|
+
purple: Flowbite::Style.new(
|
|
51
|
+
default: ["focus:outline-none", "text-white", "bg-purple-700", "hover:bg-purple-800", "focus:ring-4", "focus:ring-purple-300", "font-medium", "rounded-lg", "dark:bg-purple-600", "dark:hover:bg-purple-700", "dark:focus:ring-purple-900"]
|
|
52
|
+
),
|
|
53
|
+
red: Flowbite::Style.new(
|
|
54
|
+
default: ["focus:outline-none", "text-white", "bg-red-700", "hover:bg-red-800", "focus:ring-4", "focus:ring-red-300", "font-medium", "rounded-lg", "dark:bg-red-600", "dark:hover:bg-red-700", "dark:focus:ring-red-900"]
|
|
55
|
+
),
|
|
56
|
+
yellow: Flowbite::Style.new(
|
|
57
|
+
default: ["focus:outline-none", "text-white", "bg-yellow-400", "hover:bg-yellow-500", "focus:ring-4", "focus:ring-yellow-300", "font-medium", "rounded-lg", "dark:focus:ring-yellow-900"]
|
|
58
|
+
)
|
|
59
|
+
}.freeze
|
|
60
|
+
end
|
|
61
|
+
# rubocop:enable Layout/LineLength
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
attr_reader :button_attributes, :size, :style
|
|
65
|
+
|
|
66
|
+
def initialize(size: :default, style: :default, **button_attributes)
|
|
67
|
+
@size = size
|
|
68
|
+
@style = style
|
|
69
|
+
@button_attributes = button_attributes
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def call
|
|
73
|
+
content_tag(
|
|
74
|
+
:button,
|
|
75
|
+
content,
|
|
76
|
+
**options
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def classes
|
|
83
|
+
self.class.classes(size: size, state: :default, style: style)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def options
|
|
87
|
+
{
|
|
88
|
+
class: classes
|
|
89
|
+
}.merge(button_attributes)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
# Renders a card element.
|
|
5
|
+
#
|
|
6
|
+
# See https://flowbite.com/docs/components/cards/
|
|
7
|
+
class Card < ViewComponent::Base
|
|
8
|
+
class << self
|
|
9
|
+
def classes(state: :default, style: :default)
|
|
10
|
+
style = styles.fetch(style)
|
|
11
|
+
style.fetch(state)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# rubocop:disable Layout/LineLength
|
|
15
|
+
def styles
|
|
16
|
+
{
|
|
17
|
+
default: Flowbite::Style.new(
|
|
18
|
+
default: ["p-6", "bg-white", "border", "border-gray-200", "rounded-lg", "shadow-sm", "dark:bg-gray-800", "dark:border-gray-700"]
|
|
19
|
+
)
|
|
20
|
+
}.freeze
|
|
21
|
+
end
|
|
22
|
+
# rubocop:enable Layout/LineLength
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def call
|
|
26
|
+
card_options = {}
|
|
27
|
+
card_options[:class] = self.class.classes + @class
|
|
28
|
+
|
|
29
|
+
content_tag(:div, card_options.merge(@options)) do
|
|
30
|
+
concat(content_tag(:div, content, class: "font-normal text-gray-700 dark:text-gray-400"))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param class [Array<String>] Additional CSS classes for the card
|
|
35
|
+
# container.
|
|
36
|
+
#
|
|
37
|
+
# @param options [Hash] Additional HTML options for the card container
|
|
38
|
+
# (e.g., custom classes, data attributes). These options are merged into
|
|
39
|
+
# the card's root element.
|
|
40
|
+
def initialize(class: [], options: {})
|
|
41
|
+
@class = Array(binding.local_variable_get(:class)) || []
|
|
42
|
+
@options = options || {}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Flowbite
|
|
4
|
+
module Input
|
|
5
|
+
# The checkbox component can be used to receive one or more selected options
|
|
6
|
+
# from the user in the form of a square box available in multiple styles,
|
|
7
|
+
# sizes, colors, and variants coded with the utility classes from Tailwind
|
|
8
|
+
# CSS and with support for dark mode.
|
|
9
|
+
#
|
|
10
|
+
# https://flowbite.com/docs/forms/checkbox/
|
|
11
|
+
class Checkbox < Field
|
|
12
|
+
DEFAULT_CHECKED_VALUE = "1"
|
|
13
|
+
DEFAULT_UNCHECKED_VALUE = "0"
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
# Checkboxes only have their default size.
|
|
17
|
+
def sizes
|
|
18
|
+
{
|
|
19
|
+
default: ["w-4", "h-4"]
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# rubocop:disable Layout/LineLength
|
|
24
|
+
def styles
|
|
25
|
+
{
|
|
26
|
+
default: Flowbite::Style.new(
|
|
27
|
+
default: ["text-blue-600", "bg-gray-100", "border-gray-300", "rounded-sm", "focus:ring-blue-500", "dark:focus:ring-blue-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-gray-600"],
|
|
28
|
+
disabled: ["text-blue-600", "bg-gray-100", "border-gray-300", "rounded-sm", "focus:ring-blue-500", "dark:focus:ring-blue-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-gray-600"],
|
|
29
|
+
error: ["text-red-600", "bg-red-50", "border-red-500", "rounded-sm", "focus:ring-red-500", "dark:focus:ring-red-600", "dark:ring-offset-gray-800", "focus:ring-2", "dark:bg-gray-700", "dark:border-red-500"]
|
|
30
|
+
)
|
|
31
|
+
}.freeze
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns the HTML to use for the actual input field element.
|
|
36
|
+
def call
|
|
37
|
+
@form.send(
|
|
38
|
+
input_field_type,
|
|
39
|
+
@attribute,
|
|
40
|
+
input_options,
|
|
41
|
+
checked_value,
|
|
42
|
+
unchecked_value
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def initialize(attribute:, form:, disabled: false, options: {}, size: :default, unchecked_value: DEFAULT_UNCHECKED_VALUE, value: DEFAULT_CHECKED_VALUE)
|
|
47
|
+
super(attribute: attribute, form: form, disabled: disabled, options: options, size: size)
|
|
48
|
+
@unchecked_value = unchecked_value
|
|
49
|
+
@value = value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def input_field_type
|
|
53
|
+
:check_box
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Returns the options argument for the input field
|
|
57
|
+
def input_options
|
|
58
|
+
{
|
|
59
|
+
class: classes,
|
|
60
|
+
disabled: disabled?
|
|
61
|
+
}.merge(options)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def checked_value
|
|
67
|
+
@value
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
attr_reader :unchecked_value
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|