solidus_admin 0.0.0 → 0.0.2
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 +5 -5
- data/LICENSE +7 -0
- data/README.md +31 -0
- data/Rakefile +21 -0
- data/app/assets/config/solidus_admin_manifest.js +4 -0
- data/app/assets/images/solidus_admin/.keep +0 -0
- data/app/assets/images/solidus_admin/arrow_down_s_fill_gray_700.svg +3 -0
- data/app/assets/images/solidus_admin/arrow_down_s_fill_red_400.svg +3 -0
- data/app/assets/images/solidus_admin/arrow_right_up_line.svg +5 -0
- data/app/assets/images/solidus_admin/favicon.ico +0 -0
- data/app/assets/images/solidus_admin/remixicon.symbol.svg +11 -0
- data/app/assets/stylesheets/solidus_admin/application.css +3 -0
- data/app/assets/stylesheets/solidus_admin/application.tailwind.css.erb +35 -0
- data/app/components/solidus_admin/base_component.rb +42 -0
- data/app/components/solidus_admin/feedback/component.html.erb +11 -0
- data/app/components/solidus_admin/feedback/component.rb +4 -0
- data/app/components/solidus_admin/feedback/component.yml +5 -0
- data/app/components/solidus_admin/orders/index/component.html.erb +31 -0
- data/app/components/solidus_admin/orders/index/component.rb +118 -0
- data/app/components/solidus_admin/orders/index/component.yml +13 -0
- data/app/components/solidus_admin/products/index/component.html.erb +30 -0
- data/app/components/solidus_admin/products/index/component.rb +126 -0
- data/app/components/solidus_admin/products/index/component.yml +13 -0
- data/app/components/solidus_admin/products/show/component.html.erb +149 -0
- data/app/components/solidus_admin/products/show/component.js +9 -0
- data/app/components/solidus_admin/products/show/component.rb +26 -0
- data/app/components/solidus_admin/products/show/component.yml +17 -0
- data/app/components/solidus_admin/products/status/component.rb +31 -0
- data/app/components/solidus_admin/products/status/component.yml +3 -0
- data/app/components/solidus_admin/sidebar/account_nav/component.html.erb +67 -0
- data/app/components/solidus_admin/sidebar/account_nav/component.rb +15 -0
- data/app/components/solidus_admin/sidebar/account_nav/component.yml +3 -0
- data/app/components/solidus_admin/sidebar/component.html.erb +39 -0
- data/app/components/solidus_admin/sidebar/component.js +14 -0
- data/app/components/solidus_admin/sidebar/component.rb +21 -0
- data/app/components/solidus_admin/sidebar/component.yml +2 -0
- data/app/components/solidus_admin/sidebar/item/component.html.erb +26 -0
- data/app/components/solidus_admin/sidebar/item/component.rb +27 -0
- data/app/components/solidus_admin/skip_link/component.rb +24 -0
- data/app/components/solidus_admin/skip_link/component.yml +2 -0
- data/app/components/solidus_admin/ui/badge/component.rb +34 -0
- data/app/components/solidus_admin/ui/button/component.rb +101 -0
- data/app/components/solidus_admin/ui/forms/checkbox/component.rb +42 -0
- data/app/components/solidus_admin/ui/forms/field/component.html.erb +28 -0
- data/app/components/solidus_admin/ui/forms/field/component.rb +72 -0
- data/app/components/solidus_admin/ui/forms/input/component.js +16 -0
- data/app/components/solidus_admin/ui/forms/input/component.rb +99 -0
- data/app/components/solidus_admin/ui/forms/switch/component.rb +47 -0
- data/app/components/solidus_admin/ui/icon/component.rb +25 -0
- data/app/components/solidus_admin/ui/icon/names.txt +2494 -0
- data/app/components/solidus_admin/ui/panel/component.html.erb +36 -0
- data/app/components/solidus_admin/ui/panel/component.js +14 -0
- data/app/components/solidus_admin/ui/panel/component.rb +19 -0
- data/app/components/solidus_admin/ui/panel/component.yml +4 -0
- data/app/components/solidus_admin/ui/tab/component.rb +43 -0
- data/app/components/solidus_admin/ui/table/component.html.erb +170 -0
- data/app/components/solidus_admin/ui/table/component.js +118 -0
- data/app/components/solidus_admin/ui/table/component.rb +150 -0
- data/app/components/solidus_admin/ui/table/component.yml +11 -0
- data/app/components/solidus_admin/ui/table/pagination/component.html.erb +28 -0
- data/app/components/solidus_admin/ui/table/pagination/component.rb +14 -0
- data/app/components/solidus_admin/ui/table/pagination/component.yml +3 -0
- data/app/components/solidus_admin/ui/toast/component.html.erb +26 -0
- data/app/components/solidus_admin/ui/toast/component.js +17 -0
- data/app/components/solidus_admin/ui/toast/component.rb +18 -0
- data/app/components/solidus_admin/ui/toast/component.yml +4 -0
- data/app/components/solidus_admin/ui/toggletip/component.html.erb +53 -0
- data/app/components/solidus_admin/ui/toggletip/component.js +26 -0
- data/app/components/solidus_admin/ui/toggletip/component.rb +98 -0
- data/app/components/solidus_admin/ui/toggletip/component.yml +2 -0
- data/app/controllers/solidus_admin/accounts_controller.rb +11 -0
- data/app/controllers/solidus_admin/authentication_adapters/backend.rb +26 -0
- data/app/controllers/solidus_admin/base_controller.rb +21 -0
- data/app/controllers/solidus_admin/controller_helpers/authentication.rb +31 -0
- data/app/controllers/solidus_admin/controller_helpers/authorization.rb +29 -0
- data/app/controllers/solidus_admin/controller_helpers/locale.rb +32 -0
- data/app/controllers/solidus_admin/orders_controller.rb +21 -0
- data/app/controllers/solidus_admin/products_controller.rb +93 -0
- data/app/helpers/solidus_admin/components_helper.rb +9 -0
- data/app/helpers/solidus_admin/layout_helper.rb +18 -0
- data/app/javascript/solidus_admin/application.js +2 -0
- data/app/javascript/solidus_admin/controllers/application.js +9 -0
- data/app/javascript/solidus_admin/controllers/components.js +35 -0
- data/app/javascript/solidus_admin/controllers/hello_controller.js +7 -0
- data/app/javascript/solidus_admin/controllers/index.js +14 -0
- data/app/javascript/solidus_admin/utils.js +8 -0
- data/app/views/layouts/solidus_admin/application.html.erb +30 -0
- data/app/views/layouts/solidus_admin/preview.html.erb +10 -0
- data/app/views/solidus_admin/.keep +0 -0
- data/bin/rails +13 -0
- data/config/importmap.rb +13 -0
- data/config/locales/main_nav.en.yml +13 -0
- data/config/locales/orders.en.yml +4 -0
- data/config/locales/products.en.yml +10 -0
- data/config/routes.rb +13 -0
- data/config/solidus_admin/tailwind.config.js.erb +95 -0
- data/docs/customizing_main_navigation.md +42 -0
- data/docs/customizing_tailwind.md +78 -0
- data/docs/customizing_view_components.md +153 -0
- data/lib/generators/solidus_admin/component/USAGE +13 -0
- data/lib/generators/solidus_admin/component/component_generator.rb +130 -0
- data/lib/generators/solidus_admin/component/templates/component.html.erb.tt +3 -0
- data/lib/generators/solidus_admin/component/templates/component.js.tt +14 -0
- data/lib/generators/solidus_admin/component/templates/component.rb.tt +14 -0
- data/lib/generators/solidus_admin/component/templates/component.yml.tt +4 -0
- data/lib/generators/solidus_admin/component/templates/component_preview.rb.tt +15 -0
- data/lib/generators/solidus_admin/component/templates/component_preview_overview.html.erb +7 -0
- data/lib/generators/solidus_admin/component/templates/component_spec.rb.tt +16 -0
- data/lib/generators/solidus_admin/install/install_generator.rb +44 -0
- data/lib/generators/solidus_admin/install/templates/config/initializers/solidus_admin.rb +44 -0
- data/lib/solidus_admin/configuration.rb +217 -0
- data/lib/solidus_admin/engine.rb +67 -0
- data/lib/solidus_admin/importmap.rb +26 -0
- data/lib/solidus_admin/main_nav_item.rb +97 -0
- data/lib/solidus_admin/preview.rb +81 -0
- data/lib/solidus_admin/tailwindcss.rb +58 -0
- data/lib/solidus_admin/version.rb +5 -0
- data/lib/solidus_admin.rb +15 -0
- data/lib/tasks/importmap.rake +10 -0
- data/lib/tasks/tailwindcss.rake +55 -0
- data/solidus_admin.gemspec +35 -0
- metadata +255 -18
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<details class="relative w-full" aria-label="<%= t('.account') %>">
|
|
2
|
+
<summary
|
|
3
|
+
class="
|
|
4
|
+
flex gap-1.5
|
|
5
|
+
p-3 mt-2 rounded
|
|
6
|
+
body-small-bold text-gray-500
|
|
7
|
+
hover:bg-gray-25 [[open]_>_&]:bg-gray-25
|
|
8
|
+
cursor-pointer
|
|
9
|
+
[&::marker]:hidden
|
|
10
|
+
[&::-webkit-details-marker]:hidden
|
|
11
|
+
">
|
|
12
|
+
<%= icon_tag("user-smile-fill", class: "inline-block align-text-bottom shrink-0 w-6 h-6 rounded-[4.81rem] body-small fill-yellow bg-black") %>
|
|
13
|
+
<span class="overflow-hidden whitespace-nowrap text-ellipsis">
|
|
14
|
+
<%= @user_label %>
|
|
15
|
+
</span>
|
|
16
|
+
</summary>
|
|
17
|
+
|
|
18
|
+
<ul
|
|
19
|
+
class="
|
|
20
|
+
p-2 mb-1 absolute bottom-full left-0 w-full
|
|
21
|
+
body-small text-black bg-white
|
|
22
|
+
border border-gray-100 rounded-lg
|
|
23
|
+
shadow-base
|
|
24
|
+
">
|
|
25
|
+
|
|
26
|
+
<% if (available_locales = Spree.i18n_available_locales).any? %>
|
|
27
|
+
<li class="h-8 flex items-center hover:bg-gray-25 rounded">
|
|
28
|
+
<%= form_tag request.fullpath, method: :get do %>
|
|
29
|
+
<label class="flex gap-2 items-center px-2">
|
|
30
|
+
<%= icon_tag("global-line", class: "w-full max-w-[20px] h-5 fill-current shrink") %>
|
|
31
|
+
<select class="w-full appearance-none grow bg-transparent outline-none" onchange="this.form.requestSubmit()" name="switch_to_locale">
|
|
32
|
+
<%= options_for_select(
|
|
33
|
+
available_locales
|
|
34
|
+
.map do |locale|
|
|
35
|
+
[
|
|
36
|
+
t(
|
|
37
|
+
"spree.i18n.this_file_language",
|
|
38
|
+
locale: locale,
|
|
39
|
+
default: locale.to_s,
|
|
40
|
+
fallback: false
|
|
41
|
+
),
|
|
42
|
+
locale
|
|
43
|
+
]
|
|
44
|
+
end
|
|
45
|
+
.sort,
|
|
46
|
+
selected: I18n.locale,
|
|
47
|
+
) %>
|
|
48
|
+
</select>
|
|
49
|
+
<%= icon_tag("expand-up-down-line", class: "w-full max-w-[20px] h-5 fill-current shrink") %>
|
|
50
|
+
</label>
|
|
51
|
+
<% end %>
|
|
52
|
+
</li>
|
|
53
|
+
<% end %>
|
|
54
|
+
<li class="h-8 flex items-center hover:bg-gray-25 rounded">
|
|
55
|
+
<%= link_to @account_path, class: 'flex gap-2 items-center px-2' do %>
|
|
56
|
+
<%= icon_tag("user-3-line", class: "w-5 h-5 fill-current shrink") %>
|
|
57
|
+
<span><%= t('.account') %></span>
|
|
58
|
+
<% end %>
|
|
59
|
+
</li>
|
|
60
|
+
<li class="h-8 flex items-center hover:bg-gray-25 rounded">
|
|
61
|
+
<%= button_to @logout_path, method: @logout_method, class: 'flex gap-2 items-center px-2' do %>
|
|
62
|
+
<%= icon_tag("logout-box-line", class: "w-5 h-5 fill-current shrink") %>
|
|
63
|
+
<span><%= t('.logout') %></span>
|
|
64
|
+
<% end %>
|
|
65
|
+
</li>
|
|
66
|
+
</ul>
|
|
67
|
+
</details>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Account navigation
|
|
4
|
+
class SolidusAdmin::Sidebar::AccountNav::Component < SolidusAdmin::BaseComponent
|
|
5
|
+
# @param user_label [String]
|
|
6
|
+
# @param account_path [String]
|
|
7
|
+
# @param logout_path [String]
|
|
8
|
+
# @param logout_method [Symbol]
|
|
9
|
+
def initialize(user_label:, account_path:, logout_path:, logout_method:)
|
|
10
|
+
@user_label = user_label
|
|
11
|
+
@account_path = account_path
|
|
12
|
+
@logout_path = logout_path
|
|
13
|
+
@logout_method = logout_method
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<nav class="
|
|
2
|
+
flex flex-col
|
|
3
|
+
bg-gray-15
|
|
4
|
+
p-4
|
|
5
|
+
w-full
|
|
6
|
+
" data-controller="<%= stimulus_id %>" data-<%= stimulus_id %>-cookie-value="solidus_admin">
|
|
7
|
+
<%= link_to @store.url, class: "py-3 px-2 text-left flex mb-4" do %>
|
|
8
|
+
<%= image_tag @logo_path, alt: t('.visit_store'), class: "max-h-7" %>
|
|
9
|
+
<% end %>
|
|
10
|
+
|
|
11
|
+
<%= link_to @store.url, target: :_blank, class: "flex mb-4 px-2 py-1.5 border border-gray-100 rounded-sm shadow-sm" do %>
|
|
12
|
+
<div class="flex-grow">
|
|
13
|
+
<p class="body-small-bold text-black"><%= @store.name %></p>
|
|
14
|
+
<p class="body-tiny text-gray-500"><%= @store.url %></p>
|
|
15
|
+
</div>
|
|
16
|
+
<%= render component("ui/icon").new(name: 'arrow-right-up-line', class: 'w-4 h-4 fill-gray-400') %>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<ul>
|
|
20
|
+
<%= render component("sidebar/item").with_collection(items, fullpath: request.fullpath) %>
|
|
21
|
+
</ul>
|
|
22
|
+
|
|
23
|
+
<div class="mt-auto">
|
|
24
|
+
<div class="group mb-3">
|
|
25
|
+
<label class="flex gap-3 items-center py-0.5 px-3 pb-0.5 rounded hover:text-red-500 hover:bg-gray-50 body-small-bold text-black cursor-pointer">
|
|
26
|
+
<%= t('spree.navigation.switch_to_legacy') %>
|
|
27
|
+
<div class="flex items-center">
|
|
28
|
+
<%= render component("ui/forms/switch").new(size: :s, checked: false, 'data-action': "#{stimulus_id}#setCookie:prevent") %>
|
|
29
|
+
</div>
|
|
30
|
+
</label>
|
|
31
|
+
</div>
|
|
32
|
+
<%= render component("sidebar/account_nav").new(
|
|
33
|
+
user_label: helpers.current_solidus_admin_user.email,
|
|
34
|
+
account_path: solidus_admin.account_path,
|
|
35
|
+
logout_path: helpers.solidus_admin_logout_path,
|
|
36
|
+
logout_method: helpers.solidus_admin_logout_method,
|
|
37
|
+
) %>
|
|
38
|
+
</div>
|
|
39
|
+
</nav>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static values = {
|
|
5
|
+
cookie: String
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
setCookie(event) {
|
|
9
|
+
let value = !event.currentTarget.checked
|
|
10
|
+
|
|
11
|
+
document.cookie = `${this.cookieValue}=${value}; Path=/`
|
|
12
|
+
location.reload()
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Renders the sidebar
|
|
4
|
+
class SolidusAdmin::Sidebar::Component < SolidusAdmin::BaseComponent
|
|
5
|
+
def initialize(
|
|
6
|
+
store:,
|
|
7
|
+
logo_path: SolidusAdmin::Config.logo_path,
|
|
8
|
+
items: SolidusAdmin::Config.menu_items
|
|
9
|
+
)
|
|
10
|
+
@logo_path = logo_path
|
|
11
|
+
@items = items.map do |attrs|
|
|
12
|
+
children = attrs[:children].to_a.map { SolidusAdmin::MainNavItem.new(**_1, top_level: false) }
|
|
13
|
+
SolidusAdmin::MainNavItem.new(**attrs, children: children, top_level: true)
|
|
14
|
+
end
|
|
15
|
+
@store = store
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def items
|
|
19
|
+
@items.sort_by(&:position)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<li class="group <%= "active" if active? %>">
|
|
2
|
+
<a
|
|
3
|
+
href="<%= path %>"
|
|
4
|
+
aria-current="<%= @item.current?(@url_helpers, @fullpath) ? "page" : "false" %>"
|
|
5
|
+
class="
|
|
6
|
+
flex gap-3 items-center
|
|
7
|
+
py-0.5 px-3 mb-0.5 rounded
|
|
8
|
+
hover:text-red-500 hover:bg-gray-50
|
|
9
|
+
<%= "text-red-500 bg-gray-50" if active? %>
|
|
10
|
+
<%= @item.top_level ? "body-small-bold text-black" : "body-small text-gray-600" %>
|
|
11
|
+
"
|
|
12
|
+
>
|
|
13
|
+
<i class="w-[1.125rem] h-[1.125rem] body-small flex">
|
|
14
|
+
<% if @item.icon %>
|
|
15
|
+
<%= icon_tag(@item.icon, class: "inline-block w-full h-full fill-current") %>
|
|
16
|
+
<% end %>
|
|
17
|
+
</i>
|
|
18
|
+
<%= @item.name %>
|
|
19
|
+
</a>
|
|
20
|
+
|
|
21
|
+
<% if @item.children? %>
|
|
22
|
+
<ul class="<%= "hidden" unless active? %>">
|
|
23
|
+
<%= render self.class.with_collection(@item.children, url_helpers: @url_helpers, fullpath: @fullpath) %>
|
|
24
|
+
</ul>
|
|
25
|
+
<% end %>
|
|
26
|
+
</li>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Menu item within a {Sidebar}
|
|
4
|
+
class SolidusAdmin::Sidebar::Item::Component < SolidusAdmin::BaseComponent
|
|
5
|
+
with_collection_parameter :item
|
|
6
|
+
|
|
7
|
+
# @param item [SolidusAdmin::MainNavItem
|
|
8
|
+
# @param fullpath [String] the current path
|
|
9
|
+
# @param url_helpers [#solidus_admin, #spree] context for generating paths
|
|
10
|
+
def initialize(
|
|
11
|
+
item:,
|
|
12
|
+
fullpath: "#",
|
|
13
|
+
url_helpers: Struct.new(:spree, :solidus_admin).new(spree, solidus_admin)
|
|
14
|
+
)
|
|
15
|
+
@item = item
|
|
16
|
+
@url_helpers = url_helpers
|
|
17
|
+
@fullpath = fullpath
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def path
|
|
21
|
+
@item.path(@url_helpers)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def active?
|
|
25
|
+
@item.active?(@url_helpers, @fullpath)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Skip to content link
|
|
4
|
+
class SolidusAdmin::SkipLink::Component < SolidusAdmin::BaseComponent
|
|
5
|
+
# @param href [String] the href attribute for the skip link
|
|
6
|
+
def initialize(href:)
|
|
7
|
+
@href = href
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call
|
|
11
|
+
link_to t(".skip_link"),
|
|
12
|
+
@href,
|
|
13
|
+
class: %{
|
|
14
|
+
sr-only
|
|
15
|
+
focus:not-sr-only
|
|
16
|
+
inline-block
|
|
17
|
+
focus:p-2
|
|
18
|
+
focus:absolute
|
|
19
|
+
body-small
|
|
20
|
+
text-white
|
|
21
|
+
bg-black
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SolidusAdmin::UI::Badge::Component < SolidusAdmin::BaseComponent
|
|
4
|
+
include ViewComponent::InlineTemplate
|
|
5
|
+
|
|
6
|
+
COLORS = {
|
|
7
|
+
graphite_light: "text-black bg-graphiteLight",
|
|
8
|
+
red: 'text-red-500 bg-red-100',
|
|
9
|
+
green: 'text-forest bg-seafoam',
|
|
10
|
+
blue: 'text-blue bg-sky',
|
|
11
|
+
black: 'text-white bg-black',
|
|
12
|
+
yellow: 'text-orange bg-papayaWhip',
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
SIZES = {
|
|
16
|
+
s: 'leading-4 px-2 py-0.5 text-3 font-[500]',
|
|
17
|
+
m: 'leading-5 px-3 py-0.5 text-3.5 font-[500]',
|
|
18
|
+
l: 'leading-6 px-3 py-0.5 text-4 font-[500]',
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
def initialize(name:, color: :graphite_light, size: :m)
|
|
22
|
+
@name = name
|
|
23
|
+
|
|
24
|
+
@class_name = [
|
|
25
|
+
'inline-flex items-center rounded-full whitespace-nowrap', # layout
|
|
26
|
+
SIZES.fetch(size.to_sym), # size
|
|
27
|
+
COLORS.fetch(color.to_sym), # color
|
|
28
|
+
].join(' ')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
erb_template <<~ERB
|
|
32
|
+
<div class="<%= @class_name %>"><%= @name %></div>
|
|
33
|
+
ERB
|
|
34
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SolidusAdmin::UI::Button::Component < SolidusAdmin::BaseComponent
|
|
4
|
+
SIZES = {
|
|
5
|
+
s: %w[
|
|
6
|
+
h-7 w-7 p-1
|
|
7
|
+
text-xs font-semibold leading-none
|
|
8
|
+
],
|
|
9
|
+
m: %w[
|
|
10
|
+
h-9 w-9 p-1.5
|
|
11
|
+
text-sm font-semibold leading-none
|
|
12
|
+
],
|
|
13
|
+
l: %w[
|
|
14
|
+
h-12 w-12 p-2
|
|
15
|
+
text-base font-semibold leading-none
|
|
16
|
+
],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
TEXT_PADDINGS = {
|
|
20
|
+
s: %w[px-1.5 w-auto],
|
|
21
|
+
m: %w[px-3 w-auto],
|
|
22
|
+
l: %w[px-4 w-auto],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
ICON_SIZES = {
|
|
26
|
+
s: %w[w-[1.4em] h-[1.4em]],
|
|
27
|
+
m: %w[w-[1.35em] h-[1.35em]],
|
|
28
|
+
l: %w[w-[1.5em] h-[1.5em]],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
SCHEMES = {
|
|
32
|
+
primary: %w[
|
|
33
|
+
text-white bg-black
|
|
34
|
+
hover:text-white hover:bg-gray-600
|
|
35
|
+
active:text-white active:bg-gray-800
|
|
36
|
+
focus:text-white focus:bg-gray-700
|
|
37
|
+
disabled:text-gray-400 disabled:bg-gray-100 disabled:cursor-not-allowed
|
|
38
|
+
aria-disabled:text-gray-400 aria-disabled:bg-gray-100 aria-disabled:aria-disabled:cursor-not-allowed
|
|
39
|
+
],
|
|
40
|
+
secondary: %w[
|
|
41
|
+
text-gray-700 bg-white border border-1 border-gray-200
|
|
42
|
+
hover:bg-gray-50
|
|
43
|
+
active:bg-gray-100
|
|
44
|
+
focus:bg-gray-50
|
|
45
|
+
disabled:text-gray-300 disabled:bg-white disabled:cursor-not-allowed
|
|
46
|
+
aria-disabled:text-gray-300 aria-disabled:bg-white aria-disabled:cursor-not-allowed
|
|
47
|
+
],
|
|
48
|
+
danger: %w[
|
|
49
|
+
text-red-500 bg-white border border-1 border-red-500
|
|
50
|
+
hover:bg-red-500 hover:border-red-600 hover:text-white
|
|
51
|
+
active:bg-red-600 active:border-red-700 active:text-white
|
|
52
|
+
focus:bg-red-50 focus:bg-red-500 focus:border-red-600 focus:text-white
|
|
53
|
+
disabled:text-red-300 disabled:bg-white disabled:border-red-200 disabled:cursor-not-allowed
|
|
54
|
+
aria-disabled:text-red-300 aria-disabled:bg-white aria-disabled:border-red-200 aria-disabled:cursor-not-allowed
|
|
55
|
+
],
|
|
56
|
+
ghost: %w[
|
|
57
|
+
text-gray-700 bg-transparent
|
|
58
|
+
hover:bg-gray-50
|
|
59
|
+
active:bg-gray-100
|
|
60
|
+
focus:bg-gray-50 focus:ring-gray-300 focus:ring-2
|
|
61
|
+
disabled:text-gray-300 disabled:bg-transparent disabled:border-gray-300 disabled:cursor-not-allowed
|
|
62
|
+
aria-disabled:text-gray-300 aria-disabled:bg-transparent aria-disabled:border-gray-300 aria-disabled:cursor-not-allowed
|
|
63
|
+
],
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def initialize(
|
|
67
|
+
tag: :button,
|
|
68
|
+
text: nil,
|
|
69
|
+
icon: nil,
|
|
70
|
+
size: :m,
|
|
71
|
+
scheme: :primary,
|
|
72
|
+
**attributes
|
|
73
|
+
)
|
|
74
|
+
@tag = tag
|
|
75
|
+
@text = text
|
|
76
|
+
@icon = icon
|
|
77
|
+
@attributes = attributes
|
|
78
|
+
|
|
79
|
+
@attributes[:class] = [
|
|
80
|
+
'justify-start items-center justify-center gap-1 inline-flex rounded',
|
|
81
|
+
'focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0 [&:focus-visible]:outline-none',
|
|
82
|
+
SIZES.fetch(size.to_sym),
|
|
83
|
+
(TEXT_PADDINGS.fetch(size.to_sym) if @text),
|
|
84
|
+
SCHEMES.fetch(scheme.to_sym),
|
|
85
|
+
@attributes[:class],
|
|
86
|
+
].join(' ')
|
|
87
|
+
|
|
88
|
+
@icon_classes = [
|
|
89
|
+
'fill-current',
|
|
90
|
+
ICON_SIZES.fetch(size.to_sym),
|
|
91
|
+
]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def call
|
|
95
|
+
content = []
|
|
96
|
+
content << render(component('ui/icon').new(name: @icon, class: @icon_classes)) if @icon
|
|
97
|
+
content << @text if @text
|
|
98
|
+
|
|
99
|
+
content_tag(@tag, safe_join(content), **@attributes)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SolidusAdmin::UI::Forms::Checkbox::Component < SolidusAdmin::BaseComponent
|
|
4
|
+
SIZES = {
|
|
5
|
+
s: 'w-4 h-4',
|
|
6
|
+
m: 'w-5 h-5',
|
|
7
|
+
}.freeze
|
|
8
|
+
|
|
9
|
+
def initialize(size: :m, **attributes)
|
|
10
|
+
@size = size
|
|
11
|
+
@attributes = attributes
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
tag.input(
|
|
16
|
+
type: 'checkbox',
|
|
17
|
+
class: "
|
|
18
|
+
#{SIZES.fetch(@size)}
|
|
19
|
+
form-checkbox
|
|
20
|
+
cursor-pointer
|
|
21
|
+
disabled:cursor-not-allowed
|
|
22
|
+
|
|
23
|
+
bg-white rounded border border-2 border-gray-300
|
|
24
|
+
hover:border-gray-700
|
|
25
|
+
focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0
|
|
26
|
+
active:ring active:ring-gray-300 active:ring-0.5
|
|
27
|
+
disabled:border-gray-300
|
|
28
|
+
|
|
29
|
+
indeterminate:border-gray-700 indeterminate:bg-gray-700 indeterminate:text-white
|
|
30
|
+
indeterminate:hover:border-gray-500 indeterminate:hover:bg-gray-500
|
|
31
|
+
indeterminate:focus:bg-gray-700
|
|
32
|
+
indeterminate:disabled:border-gray-300 indeterminate:disabled:bg-gray-300
|
|
33
|
+
|
|
34
|
+
checked:border-gray-700 checked:bg-gray-700 checked:text-white
|
|
35
|
+
checked:hover:border-gray-500 checked:hover:bg-gray-500
|
|
36
|
+
checked:focus:bg-gray-700
|
|
37
|
+
checked:disabled:border-gray-300 checked:disabled:bg-gray-300
|
|
38
|
+
",
|
|
39
|
+
**@attributes,
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<label class="flex flex-col gap-2 w-full">
|
|
2
|
+
<div class="flex gap-1 items-center">
|
|
3
|
+
<span class="
|
|
4
|
+
text-gray-700
|
|
5
|
+
body-tiny-bold
|
|
6
|
+
body-text-xs-semibold
|
|
7
|
+
"><%= @label %></span>
|
|
8
|
+
|
|
9
|
+
<%= render component('ui/toggletip').new(text: @tip) if @tip.present? %>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<% if @input_attributes.present? %>
|
|
13
|
+
<%= render component('ui/forms/input').new(**@input_attributes) %>
|
|
14
|
+
<% else %>
|
|
15
|
+
<%= content %>
|
|
16
|
+
<% end %>
|
|
17
|
+
|
|
18
|
+
<% if @hint.present? || @error.present? %>
|
|
19
|
+
<div class="
|
|
20
|
+
body-tiny
|
|
21
|
+
[:disabled~&]:text-gray-300 text-gray-500
|
|
22
|
+
flex gap-1 flex-col
|
|
23
|
+
">
|
|
24
|
+
<%= tag.span @hint if @hint.present? %>
|
|
25
|
+
<%= tag.span safe_join(@error, tag.br), class: "text-red-400" if @error.present? %>
|
|
26
|
+
</div>
|
|
27
|
+
<% end %>
|
|
28
|
+
</label>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SolidusAdmin::UI::Forms::Field::Component < SolidusAdmin::BaseComponent
|
|
4
|
+
def initialize(label:, hint: nil, tip: nil, error: nil, input_attributes: nil, **attributes)
|
|
5
|
+
@label = label
|
|
6
|
+
@hint = hint
|
|
7
|
+
@tip = tip
|
|
8
|
+
@error = [error] if error.present?
|
|
9
|
+
@attributes = attributes
|
|
10
|
+
@input_attributes = input_attributes
|
|
11
|
+
|
|
12
|
+
raise ArgumentError, "provide either a block or input_attributes" if content? && input_attributes
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.text_field(form, method, hint: nil, tip: nil, size: :m, **attributes)
|
|
16
|
+
errors = form.object.errors.messages_for(method).presence
|
|
17
|
+
|
|
18
|
+
new(
|
|
19
|
+
label: form.object.class.human_attribute_name(method),
|
|
20
|
+
hint: hint,
|
|
21
|
+
tip: tip,
|
|
22
|
+
error: errors,
|
|
23
|
+
input_attributes: {
|
|
24
|
+
name: "#{form.object_name}[#{method}]",
|
|
25
|
+
tag: :input,
|
|
26
|
+
size: size,
|
|
27
|
+
value: form.object.public_send(method),
|
|
28
|
+
error: (errors.to_sentence.capitalize if errors),
|
|
29
|
+
**attributes,
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.select(form, method, choices, hint: nil, tip: nil, size: :m, **attributes)
|
|
35
|
+
errors = form.object.errors.messages_for(method).presence
|
|
36
|
+
|
|
37
|
+
new(
|
|
38
|
+
label: form.object.class.human_attribute_name(method),
|
|
39
|
+
hint: hint,
|
|
40
|
+
tip: tip,
|
|
41
|
+
error: errors,
|
|
42
|
+
input_attributes: {
|
|
43
|
+
name: "#{form.object_name}[#{method}]",
|
|
44
|
+
tag: :select,
|
|
45
|
+
choices: choices,
|
|
46
|
+
size: size,
|
|
47
|
+
value: form.object.public_send(method),
|
|
48
|
+
error: (errors.to_sentence.capitalize if errors),
|
|
49
|
+
**attributes,
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.text_area(form, method, hint: nil, tip: nil, size: :m, **attributes)
|
|
55
|
+
errors = form.object.errors.messages_for(method).presence
|
|
56
|
+
|
|
57
|
+
new(
|
|
58
|
+
label: form.object.class.human_attribute_name(method),
|
|
59
|
+
hint: hint,
|
|
60
|
+
tip: tip,
|
|
61
|
+
error: errors,
|
|
62
|
+
input_attributes: {
|
|
63
|
+
name: "#{form.object_name}[#{method}]",
|
|
64
|
+
size: size,
|
|
65
|
+
tag: :textarea,
|
|
66
|
+
value: form.object.public_send(method),
|
|
67
|
+
error: (errors.to_sentence.capitalize if errors),
|
|
68
|
+
**attributes,
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static values = {
|
|
5
|
+
customValidity: String,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
connect() {
|
|
9
|
+
if (this.customValidityValue)
|
|
10
|
+
this.element.setCustomValidity(this.customValidityValue)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
clearCustomValidity() {
|
|
14
|
+
this.element.setCustomValidity('')
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SolidusAdmin::UI::Forms::Input::Component < SolidusAdmin::BaseComponent
|
|
4
|
+
SIZES = {
|
|
5
|
+
s: "form-control-sm px-3 py-1.5 body-small",
|
|
6
|
+
m: "form-control-md px-3 py-1.5 body-small",
|
|
7
|
+
l: "form-control-lg px-3 py-1.5 body-text"
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
HEIGHTS = {
|
|
11
|
+
s: "h-7",
|
|
12
|
+
m: "h-9",
|
|
13
|
+
l: "h-12"
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
MULTILINE_HEIGHTS = {
|
|
17
|
+
s: %w[min-h-[84px]],
|
|
18
|
+
m: %w[min-h-[108px]],
|
|
19
|
+
l: %w[min-h-[144px]],
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
TYPES = Set.new(%i[
|
|
23
|
+
text
|
|
24
|
+
password
|
|
25
|
+
number
|
|
26
|
+
email
|
|
27
|
+
tel
|
|
28
|
+
url
|
|
29
|
+
search
|
|
30
|
+
color
|
|
31
|
+
date
|
|
32
|
+
datetime-local
|
|
33
|
+
month
|
|
34
|
+
week
|
|
35
|
+
time
|
|
36
|
+
]).freeze
|
|
37
|
+
|
|
38
|
+
def initialize(tag: :input, size: :m, error: nil, **attributes)
|
|
39
|
+
raise ArgumentError, "unsupported tag: #{tag}" unless %i[input textarea select].include?(tag)
|
|
40
|
+
|
|
41
|
+
specialized_classes = []
|
|
42
|
+
|
|
43
|
+
case tag
|
|
44
|
+
when :input
|
|
45
|
+
specialized_classes << "form-input"
|
|
46
|
+
specialized_classes << HEIGHTS[size]
|
|
47
|
+
if attributes[:type] && !TYPES.include?(attributes[:type])
|
|
48
|
+
raise ArgumentError, "unsupported type attribute: #{attributes[:type]}"
|
|
49
|
+
end
|
|
50
|
+
when :textarea
|
|
51
|
+
specialized_classes << "form-textarea"
|
|
52
|
+
specialized_classes << MULTILINE_HEIGHTS[size]
|
|
53
|
+
when :select
|
|
54
|
+
if attributes[:multiple]
|
|
55
|
+
specialized_classes << "form-multiselect"
|
|
56
|
+
specialized_classes << MULTILINE_HEIGHTS[size]
|
|
57
|
+
else
|
|
58
|
+
specialized_classes << "form-select"
|
|
59
|
+
specialized_classes << "bg-arrow-down-s-fill-gray-700 invalid:bg-arrow-down-s-fill-red-400 aria-invalid:bg-arrow-down-s-fill-red-400"
|
|
60
|
+
specialized_classes << HEIGHTS[size]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
attributes[:class] = [
|
|
65
|
+
%w[
|
|
66
|
+
w-full
|
|
67
|
+
text-black bg-white border border-gray-300 rounded-sm placeholder:text-gray-400
|
|
68
|
+
hover:border-gray-500
|
|
69
|
+
focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0 [&:focus-visible]:outline-none
|
|
70
|
+
disabled:bg-gray-50 disabled:text-gray-500 disabled:placeholder:text-gray-300 disabled:cursor-not-allowed
|
|
71
|
+
invalid:border-red-400 invalid:hover:border-red-400 invalid:text-red-400
|
|
72
|
+
aria-invalid:border-red-400 aria-invalid:hover:border-red-400 aria-invalid:text-red-400
|
|
73
|
+
],
|
|
74
|
+
SIZES[size],
|
|
75
|
+
specialized_classes,
|
|
76
|
+
attributes[:class],
|
|
77
|
+
].compact.join(" ")
|
|
78
|
+
|
|
79
|
+
@tag = tag
|
|
80
|
+
@size = size
|
|
81
|
+
@error = error
|
|
82
|
+
@attributes = attributes
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def call
|
|
86
|
+
if @tag == :select && @attributes[:choices]
|
|
87
|
+
with_content options_for_select(@attributes.delete(:choices), @attributes.delete(:value))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
tag.public_send(
|
|
91
|
+
@tag,
|
|
92
|
+
content,
|
|
93
|
+
"data-controller": stimulus_id,
|
|
94
|
+
"data-#{stimulus_id}-custom-validity-value": @error.presence,
|
|
95
|
+
"data-action": "#{stimulus_id}#clearCustomValidity",
|
|
96
|
+
**@attributes
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
end
|