plutonium 0.14.1 → 0.15.0.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README copy.md +1 -1
- data/README.md +1 -1
- data/app/assets/plutonium.css +1 -1
- data/app/views/{application → plutonium}/_resource_header.html copy.erb +1 -1
- data/app/views/{application → plutonium}/_resource_header.html.erb +1 -1
- data/app/views/{application → plutonium}/_resource_sidebar.html.erb +2 -0
- data/app/views/resource/_resource_details.html.erb +1 -36
- data/app/views/resource/_resource_form.html.erb +1 -5
- data/app/views/resource/_resource_table.html.erb +315 -85
- data/app/views/resource/edit.html.erb +1 -5
- data/app/views/resource/index.html.erb +1 -5
- data/app/views/resource/new.html.erb +1 -5
- data/app/views/resource/show.html.erb +1 -5
- data/config/initializers/pagy.rb +1 -0
- data/config/initializers/rabl.rb +27 -20
- data/gemfiles/rails_7.gemfile.lock +5 -1
- data/lib/generators/pu/core/install/templates/app/controllers/plutonium_controller.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/controllers/resource_controller.rb.tt +21 -1
- data/lib/generators/pu/core/install/templates/app/definitions/resource_definition.rb.tt +2 -0
- data/lib/generators/pu/core/install/templates/app/models/resource_record.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +1 -8
- data/lib/generators/pu/eject/shell/shell_generator.rb +2 -2
- data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +1 -1
- data/lib/generators/pu/lib/plutonium_generators/concerns/logger.rb +1 -1
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +5 -3
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +26 -2
- data/lib/generators/pu/pkg/{feature/feature_generator.rb → package/package_generator.rb} +4 -4
- data/lib/generators/pu/pkg/{feature → package}/templates/app/controllers/resource_controller.rb.tt +0 -2
- data/lib/generators/pu/pkg/package/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/package/templates/app/query_objects/resource_query_object.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app/app_generator.rb → portal/portal_generator.rb} +10 -8
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/concerns/controller.rb.tt +3 -7
- data/lib/generators/pu/pkg/{app → portal}/templates/app/controllers/dashboard_controller.rb.tt +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +5 -0
- data/lib/generators/pu/pkg/{app/templates/app/controllers/controller.rb.tt → portal/templates/app/controllers/resource_controller.rb.tt} +1 -1
- data/lib/generators/pu/pkg/portal/templates/app/definitions/resource_definition.rb.tt +4 -0
- data/lib/generators/pu/pkg/{app → portal}/templates/app/views/package/dashboard/index.html.erb +2 -1
- data/lib/generators/pu/res/conn/conn_generator.rb +78 -3
- data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/definitions/resource_definition.rb.tt +3 -0
- data/lib/generators/pu/res/conn/templates/app/policies/resource_policy.rb.tt +29 -1
- data/lib/generators/pu/res/conn/templates/app/presenters/resource_presenter.rb.tt +1 -1
- data/lib/generators/pu/res/conn/templates/app/query_objects/resource_query_object.rb.tt +1 -1
- data/lib/generators/pu/res/model/model_generator.rb +0 -7
- data/lib/generators/pu/res/model/templates/model.rb.tt +4 -1
- data/lib/generators/pu/res/scaffold/scaffold_generator.rb +22 -4
- data/lib/generators/pu/res/scaffold/templates/controller.rb.tt +0 -1
- data/lib/generators/pu/res/scaffold/templates/definition.rb.tt +4 -0
- data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +2 -2
- data/lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt +1 -1
- data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +270 -0
- data/lib/plutonium/action/README.md +0 -0
- data/lib/plutonium/action/base.rb +103 -0
- data/lib/plutonium/action/interactive.rb +117 -0
- data/lib/plutonium/action/route_options.rb +65 -0
- data/lib/plutonium/action/simple.rb +8 -0
- data/lib/plutonium/auth.rb +1 -1
- data/lib/plutonium/configuration.rb +0 -8
- data/lib/plutonium/core/actions/collection.rb +1 -1
- data/lib/plutonium/core/associations/renderers/factory.rb +3 -1
- data/lib/plutonium/core/controller.rb +110 -0
- data/lib/plutonium/core/controllers/authorizable.rb +12 -35
- data/lib/plutonium/core/controllers/bootable.rb +38 -7
- data/lib/plutonium/core/controllers/entity_scoping.rb +6 -2
- data/lib/plutonium/core/fields/renderers/association_renderer.rb +1 -1
- data/lib/plutonium/core/ui/collection.rb +1 -1
- data/lib/plutonium/core/ui/detail.rb +1 -1
- data/lib/plutonium/core/ui/form.rb +1 -1
- data/lib/plutonium/definition/actions.rb +50 -0
- data/lib/plutonium/definition/base.rb +92 -0
- data/lib/plutonium/definition/config_attr.rb +30 -0
- data/lib/plutonium/definition/defineable_props.rb +96 -0
- data/lib/plutonium/definition/search.rb +21 -0
- data/lib/plutonium/engine/validator.rb +30 -0
- data/lib/plutonium/engine.rb +25 -0
- data/lib/plutonium/helpers/form_helper.rb +1 -3
- data/lib/plutonium/interaction/README.md +369 -0
- data/lib/plutonium/interaction/base.rb +75 -0
- data/lib/plutonium/interaction/concerns/presentable.rb +61 -0
- data/lib/plutonium/interaction/concerns/workflow_dsl.rb +82 -0
- data/lib/plutonium/interaction/outcome.rb +129 -0
- data/lib/plutonium/interaction/response/base.rb +63 -0
- data/lib/plutonium/interaction/response/null.rb +33 -0
- data/lib/plutonium/interaction/response/redirect.rb +30 -0
- data/lib/plutonium/interaction/response/render.rb +28 -0
- data/lib/plutonium/lib/bit_flags.rb +70 -9
- data/lib/plutonium/{config → lib}/overlayed_hash.rb +1 -1
- data/lib/plutonium/lib/smart_cache.rb +171 -0
- data/lib/plutonium/models/has_cents.rb +170 -0
- data/lib/plutonium/{pkg/base.rb → package/engine.rb} +10 -2
- data/lib/plutonium/{application → portal}/controller.rb +3 -11
- data/lib/plutonium/{application → portal}/dynamic_controllers.rb +4 -4
- data/lib/plutonium/portal/engine.rb +15 -0
- data/lib/plutonium/railtie.rb +33 -1
- data/lib/plutonium/reloader.rb +5 -5
- data/lib/plutonium/resource/controller.rb +51 -34
- data/lib/plutonium/resource/controllers/authorizable.rb +128 -0
- data/lib/plutonium/{core → resource}/controllers/crud_actions.rb +23 -22
- data/lib/plutonium/resource/controllers/defineable.rb +26 -0
- data/lib/plutonium/{core → resource}/controllers/interactive_actions.rb +12 -12
- data/lib/plutonium/resource/controllers/presentable.rb +41 -0
- data/lib/plutonium/resource/controllers/queryable.rb +44 -0
- data/lib/plutonium/resource/definition.rb +6 -0
- data/lib/plutonium/resource/policy.rb +25 -13
- data/lib/plutonium/resource/query_object.rb +50 -51
- data/lib/plutonium/resource/record.rb +6 -89
- data/lib/plutonium/resource/register.rb +82 -0
- data/lib/plutonium/routing/mapper_extensions.rb +1 -1
- data/lib/plutonium/routing/resource_registration.rb +1 -1
- data/lib/plutonium/routing/route_set_extensions.rb +6 -18
- data/lib/plutonium/ui/action_button.rb +125 -0
- data/lib/plutonium/ui/breadcrumbs.rb +163 -0
- data/lib/plutonium/ui/component/base.rb +13 -0
- data/lib/plutonium/ui/component/behaviour.rb +38 -0
- data/lib/plutonium/ui/component/kit.rb +31 -0
- data/lib/plutonium/ui/component/methods.rb +54 -0
- data/lib/plutonium/ui/display/base.rb +25 -0
- data/lib/plutonium/ui/display/component/association.rb +26 -0
- data/lib/plutonium/ui/display/resource.rb +77 -0
- data/lib/plutonium/ui/display/theme.rb +27 -0
- data/lib/plutonium/ui/dyna_frame/content.rb +20 -0
- data/lib/plutonium/ui/empty_card.rb +20 -0
- data/lib/plutonium/ui/form/base.rb +37 -0
- data/lib/plutonium/ui/form/resource.rb +75 -0
- data/lib/plutonium/ui/form/theme.rb +42 -0
- data/lib/plutonium/ui/page/base.rb +112 -0
- data/lib/plutonium/ui/page/edit.rb +23 -0
- data/lib/plutonium/ui/page/index.rb +27 -0
- data/lib/plutonium/ui/page/new.rb +23 -0
- data/lib/plutonium/ui/page/show.rb +27 -0
- data/lib/plutonium/ui/page_header.rb +49 -0
- data/lib/plutonium/ui/table/base.rb +13 -0
- data/lib/plutonium/ui/table/components/pagy_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_page_info.rb +70 -0
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +105 -0
- data/lib/plutonium/ui/table/components/scopes_bar.rb +136 -0
- data/lib/plutonium/ui/table/components/search_bar.rb +158 -0
- data/lib/plutonium/ui/table/display_theme.rb +21 -0
- data/lib/plutonium/ui/table/resource.rb +98 -0
- data/lib/plutonium/ui/table/theme.rb +35 -0
- data/lib/plutonium/ui.rb +9 -0
- data/lib/plutonium/version.rb +5 -1
- data/lib/plutonium.rb +14 -1
- data/package-lock.json +19 -22
- data/package.json +4 -4
- data/src/css/plutonium.css +15 -0
- data/tailwind.options.js +11 -3
- metadata +218 -81
- data/lib/generators/pu/core/install/templates/app/presenters/resource_presenter.rb.tt +0 -2
- data/lib/generators/pu/core/install/templates/app/query_objects/resource_query_object.rb.tt +0 -2
- data/lib/generators/pu/pkg/feature/templates/app/query_objects/resource_query_object.rb.tt +0 -4
- data/lib/plutonium/concerns/resource_validatable.rb +0 -34
- data/lib/plutonium/config.rb +0 -9
- data/lib/plutonium/core/controllers/base.rb +0 -101
- data/lib/plutonium/core/controllers/presentable.rb +0 -65
- data/lib/plutonium/core/controllers/queryable.rb +0 -28
- data/lib/plutonium/pkg/app.rb +0 -35
- data/lib/plutonium/pkg/concerns/resource_validatable.rb +0 -36
- data/lib/plutonium/pkg/feature.rb +0 -18
- data/lib/plutonium/policy/initializer.rb +0 -22
- data/lib/plutonium/policy/scope.rb +0 -19
- data/lib/plutonium/pundit/context.rb +0 -18
- data/lib/plutonium/pundit/policy_finder.rb +0 -25
- data/lib/plutonium/resource/policy_context.rb +0 -5
- data/lib/plutonium/resource_register.rb +0 -83
- data/lib/plutonium/smart_cache.rb +0 -151
- /data/app/views/{application → plutonium}/_flash.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_alerts.html.erb +0 -0
- /data/app/views/{application → plutonium}/_flash_toasts.html.erb +0 -0
- /data/lib/generators/pu/pkg/{app/templates/app/views/package → package/templates}/.keep +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/interactions/resource_interaction.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/models/resource_record.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature → package}/templates/lib/engine.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/policies/resource_policy.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/presenters/resource_presenter.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/app/query_objects/resource_query_object.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{feature/templates → portal/templates/app/views/package}/.keep +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/config/routes.rb.tt +0 -0
- /data/lib/generators/pu/pkg/{app → portal}/templates/lib/engine.rb.tt +0 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Table
|
6
|
+
module Components
|
7
|
+
class PagyInfo < Plutonium::UI::Component::Base
|
8
|
+
include Pagy::Frontend
|
9
|
+
|
10
|
+
def initialize(pagy, per_page_options: [5, 10, 20, 50, 100])
|
11
|
+
@pagy = pagy
|
12
|
+
@per_page_options = (per_page_options + [@pagy.limit]).uniq.sort
|
13
|
+
end
|
14
|
+
|
15
|
+
def view_template
|
16
|
+
div(class: "flex flex-col md:flex-row justify-between items-center text-sm text-gray-500 dark:text-gray-400") do
|
17
|
+
results_info
|
18
|
+
per_page_selector
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def results_info
|
25
|
+
div do
|
26
|
+
plain "Showing "
|
27
|
+
b { @pagy.from.to_s }
|
28
|
+
plain " to "
|
29
|
+
b { @pagy.to.to_s }
|
30
|
+
plain " of "
|
31
|
+
b { @pagy.count }
|
32
|
+
plain " results"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def per_page_selector
|
37
|
+
original_attributes = Phlex::HTML::EVENT_ATTRIBUTES
|
38
|
+
temp_attributes = Phlex::HTML::EVENT_ATTRIBUTES.dup
|
39
|
+
temp_attributes.delete("onchange")
|
40
|
+
Phlex::HTML.const_set(:EVENT_ATTRIBUTES, temp_attributes)
|
41
|
+
|
42
|
+
div(class: "flex items-center space-x-2 mt-2 md:mt-0") do
|
43
|
+
label(for: "perPage", class: "mr-2") { "Per page" }
|
44
|
+
select(id: "perPage", name: "items", class: select_classes, onchange: "window.location.href=this.value") do
|
45
|
+
@per_page_options.each do |option|
|
46
|
+
option(value: page_url(option), selected: option == @pagy.limit) { option.to_s }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
ensure
|
51
|
+
# TODO: remove this once Phlex adds support for SafeValues
|
52
|
+
Phlex::HTML.const_set(:EVENT_ATTRIBUTES, original_attributes)
|
53
|
+
end
|
54
|
+
|
55
|
+
def select_classes
|
56
|
+
"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
57
|
+
end
|
58
|
+
|
59
|
+
def page_url(limit)
|
60
|
+
original_limit = @pagy.vars[:limit]
|
61
|
+
@pagy.vars[:limit] = limit
|
62
|
+
pagy_url_for(@pagy, @pagy.page)
|
63
|
+
ensure
|
64
|
+
@pagy.vars[:limit] = original_limit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Table
|
6
|
+
module Components
|
7
|
+
class PagyPageInfo < Plutonium::UI::Component::Base
|
8
|
+
include Pagy::Frontend
|
9
|
+
|
10
|
+
def initialize(pagy, per_page_options: [5, 10, 20, 50, 100])
|
11
|
+
@pagy = pagy
|
12
|
+
@per_page_options = (per_page_options + [@pagy.limit]).uniq.sort
|
13
|
+
end
|
14
|
+
|
15
|
+
def view_template
|
16
|
+
div(class: "flex flex-col md:flex-row justify-between items-center text-sm text-gray-500 dark:text-gray-400") do
|
17
|
+
results_info
|
18
|
+
per_page_selector
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def results_info
|
25
|
+
div do
|
26
|
+
plain "Showing "
|
27
|
+
b { @pagy.from.to_s }
|
28
|
+
plain " to "
|
29
|
+
b { @pagy.to.to_s }
|
30
|
+
plain " of "
|
31
|
+
b { @pagy.count }
|
32
|
+
plain " results"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def per_page_selector
|
37
|
+
original_attributes = Phlex::HTML::EVENT_ATTRIBUTES
|
38
|
+
temp_attributes = Phlex::HTML::EVENT_ATTRIBUTES.dup
|
39
|
+
temp_attributes.delete("onchange")
|
40
|
+
Phlex::HTML.const_set(:EVENT_ATTRIBUTES, temp_attributes)
|
41
|
+
|
42
|
+
div(class: "flex items-center space-x-2 mt-2 md:mt-0") do
|
43
|
+
label(for: "perPage", class: "mr-2") { "Per page" }
|
44
|
+
select(id: "perPage", name: "items", class: select_classes, onchange: "window.location.href=this.value") do
|
45
|
+
@per_page_options.each do |option|
|
46
|
+
option(value: page_url(option), selected: option == @pagy.limit) { option.to_s }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
ensure
|
51
|
+
# TODO: remove this once Phlex adds support for SafeValues
|
52
|
+
Phlex::HTML.const_set(:EVENT_ATTRIBUTES, original_attributes)
|
53
|
+
end
|
54
|
+
|
55
|
+
def select_classes
|
56
|
+
"bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
57
|
+
end
|
58
|
+
|
59
|
+
def page_url(limit)
|
60
|
+
original_limit = @pagy.vars[:limit]
|
61
|
+
@pagy.vars[:limit] = limit
|
62
|
+
pagy_url_for(@pagy, @pagy.page)
|
63
|
+
ensure
|
64
|
+
@pagy.vars[:limit] = original_limit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Table
|
6
|
+
module Components
|
7
|
+
class PagyPagination < Plutonium::UI::Component::Base
|
8
|
+
include Pagy::Frontend
|
9
|
+
|
10
|
+
def initialize(pagy)
|
11
|
+
@pagy = pagy
|
12
|
+
end
|
13
|
+
|
14
|
+
def view_template
|
15
|
+
nav(aria_label: "Page navigation", class: "flex justify-center mt-4") do
|
16
|
+
ul(class: "inline-flex -space-x-px text-sm") do
|
17
|
+
prev_link
|
18
|
+
page_links
|
19
|
+
next_link
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def prev_link
|
27
|
+
li do
|
28
|
+
if @pagy.prev
|
29
|
+
a(href: page_url(@pagy.prev), class: link_classes(true)) {
|
30
|
+
render Phlex::TablerIcons::ChevronLeft.new
|
31
|
+
}
|
32
|
+
else
|
33
|
+
a(href: "#", class: disabled_link_classes(true), aria_disabled: "true") {
|
34
|
+
render Phlex::TablerIcons::ChevronLeft.new
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def next_link
|
41
|
+
li do
|
42
|
+
if @pagy.next
|
43
|
+
a(href: page_url(@pagy.next), class: link_classes(false, true)) {
|
44
|
+
render Phlex::TablerIcons::ChevronRight.new
|
45
|
+
}
|
46
|
+
else
|
47
|
+
a(href: "#", class: disabled_link_classes(false, true), aria_disabled: "true") {
|
48
|
+
render Phlex::TablerIcons::ChevronRight.new
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def page_links
|
55
|
+
@pagy.series.each do |item|
|
56
|
+
li do
|
57
|
+
case item
|
58
|
+
when Integer
|
59
|
+
page_link(item)
|
60
|
+
when String
|
61
|
+
current_page_link(item)
|
62
|
+
when :gap
|
63
|
+
gap_link
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def page_link(page)
|
70
|
+
a(href: page_url(page), class: link_classes) { page.to_s }
|
71
|
+
end
|
72
|
+
|
73
|
+
def current_page_link(page)
|
74
|
+
a(href: "#", class: current_link_classes, aria_current: "page") { page.to_s }
|
75
|
+
end
|
76
|
+
|
77
|
+
def gap_link
|
78
|
+
a(href: "#", class: link_classes, aria_disabled: "true") { "..." }
|
79
|
+
end
|
80
|
+
|
81
|
+
def link_classes(first = false, last = false)
|
82
|
+
classes = ["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"]
|
83
|
+
classes << "rounded-s-lg" if first
|
84
|
+
classes << "rounded-e-lg" if last
|
85
|
+
classes.join(" ")
|
86
|
+
end
|
87
|
+
|
88
|
+
def current_link_classes
|
89
|
+
"flex items-center justify-center px-3 h-8 text-blue-600 border border-gray-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white"
|
90
|
+
end
|
91
|
+
|
92
|
+
def disabled_link_classes(first = false, last = false)
|
93
|
+
classes = link_classes(first, last).split
|
94
|
+
classes << "opacity-50" << "cursor-not-allowed"
|
95
|
+
classes.join(" ")
|
96
|
+
end
|
97
|
+
|
98
|
+
def page_url(page)
|
99
|
+
pagy_url_for(@pagy, page)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Table
|
6
|
+
module Components
|
7
|
+
class ScopesBar < Plutonium::UI::Component::Base
|
8
|
+
include Plutonium::UI::Component::Behaviour
|
9
|
+
|
10
|
+
def view_template
|
11
|
+
div(
|
12
|
+
class:
|
13
|
+
# "flex flex-wrap justify-between items-center gap-4 p-4 bg-white border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700 mb-4"
|
14
|
+
"flex flex-wrap justify-between items-center gap-4 mb-4"
|
15
|
+
) do
|
16
|
+
div(class: "flex flex-wrap items-center gap-2") do
|
17
|
+
name = "all"
|
18
|
+
if current_scope.blank?
|
19
|
+
a(
|
20
|
+
id: "#{name}-scope",
|
21
|
+
href: current_query_object.build_url(scope: nil),
|
22
|
+
class:
|
23
|
+
"px-4 py-2 text-sm font-medium text-white bg-primary-700 border border-primary-700 rounded-lg hover:bg-primary-800 focus:z-10 focus:ring-2 focus:ring-primary-700 focus:text-white dark:bg-primary-600 dark:border-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
24
|
+
) { name.humanize }
|
25
|
+
else
|
26
|
+
a(
|
27
|
+
id: "#{name}-scope",
|
28
|
+
href: current_query_object.build_url(scope: nil),
|
29
|
+
class:
|
30
|
+
"px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 hover:text-gray-700 focus:z-10 focus:ring-2 focus:ring-gray-300 focus:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:focus:text-white"
|
31
|
+
) { name.humanize }
|
32
|
+
end
|
33
|
+
|
34
|
+
current_query_object.scope_definitions.each do |name, definition|
|
35
|
+
if name == current_scope
|
36
|
+
a(
|
37
|
+
id: "#{name}-scope",
|
38
|
+
href: current_query_object.build_url(scope: name),
|
39
|
+
class:
|
40
|
+
"px-4 py-2 text-sm font-medium text-white bg-primary-700 border border-primary-700 rounded-lg hover:bg-primary-800 focus:z-10 focus:ring-2 focus:ring-primary-700 focus:text-white dark:bg-primary-600 dark:border-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
|
41
|
+
) { name.humanize }
|
42
|
+
else
|
43
|
+
a(
|
44
|
+
id: "#{name}-scope",
|
45
|
+
href: current_query_object.build_url(scope: name),
|
46
|
+
class:
|
47
|
+
"px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 hover:text-gray-700 focus:z-10 focus:ring-2 focus:ring-gray-300 focus:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:focus:text-white"
|
48
|
+
) { name.humanize }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# div(class: "flex flex-wrap items-center gap-2") do
|
54
|
+
# button(
|
55
|
+
# class:
|
56
|
+
# "inline-flex items-center px-3 py-2 text-sm font-medium text-white bg-red-700 rounded-lg hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:bg-red-600 dark:hover:bg-red-700 dark:focus:ring-red-800"
|
57
|
+
# ) do
|
58
|
+
# svg(
|
59
|
+
# class: "w-4 h-4 mr-2",
|
60
|
+
# fill: "none",
|
61
|
+
# stroke: "currentColor",
|
62
|
+
# viewbox: "0 0 24 24",
|
63
|
+
# xmlns: "http://www.w3.org/2000/svg"
|
64
|
+
# ) do |s|
|
65
|
+
# s.path(
|
66
|
+
# stroke_linecap: "round",
|
67
|
+
# stroke_linejoin: "round",
|
68
|
+
# stroke_width: "2",
|
69
|
+
# d:
|
70
|
+
# "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
71
|
+
# )
|
72
|
+
# end
|
73
|
+
# plain " Delete Selected "
|
74
|
+
# end
|
75
|
+
# button(
|
76
|
+
# class:
|
77
|
+
# "inline-flex items-center px-3 py-2 text-sm font-medium text-white bg-yellow-700 rounded-lg hover:bg-yellow-800 focus:ring-4 focus:outline-none focus:ring-yellow-300 dark:bg-yellow-600 dark:hover:bg-yellow-700 dark:focus:ring-yellow-800"
|
78
|
+
# ) do
|
79
|
+
# svg(
|
80
|
+
# class: "w-4 h-4 mr-2",
|
81
|
+
# fill: "none",
|
82
|
+
# stroke: "currentColor",
|
83
|
+
# viewbox: "0 0 24 24",
|
84
|
+
# xmlns: "http://www.w3.org/2000/svg"
|
85
|
+
# ) do |s|
|
86
|
+
# s.path(
|
87
|
+
# stroke_linecap: "round",
|
88
|
+
# stroke_linejoin: "round",
|
89
|
+
# stroke_width: "2",
|
90
|
+
# d:
|
91
|
+
# "M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
|
92
|
+
# )
|
93
|
+
# end
|
94
|
+
# plain " Archive Selected "
|
95
|
+
# end
|
96
|
+
# button(
|
97
|
+
# id: "dropdownActionButton",
|
98
|
+
# data_dropdown_toggle: "dropdownAction",
|
99
|
+
# class:
|
100
|
+
# "inline-flex items-center text-gray-500 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-3 py-2 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700",
|
101
|
+
# type: "button"
|
102
|
+
# ) do
|
103
|
+
# span(class: "sr-only") { "Action button" }
|
104
|
+
# plain " More Actions "
|
105
|
+
# svg(
|
106
|
+
# class: "w-2.5 h-2.5 ml-2.5",
|
107
|
+
# aria_hidden: "true",
|
108
|
+
# xmlns: "http://www.w3.org/2000/svg",
|
109
|
+
# fill: "none",
|
110
|
+
# viewbox: "0 0 10 6"
|
111
|
+
# ) do |s|
|
112
|
+
# s.path(
|
113
|
+
# stroke: "currentColor",
|
114
|
+
# stroke_linecap: "round",
|
115
|
+
# stroke_linejoin: "round",
|
116
|
+
# stroke_width: "2",
|
117
|
+
# d: "m1 1 4 4 4-4"
|
118
|
+
# )
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def current_scope = resource_query_params[:scope]
|
128
|
+
|
129
|
+
def render?
|
130
|
+
current_query_object.scope_definitions.present?
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Table
|
6
|
+
module Components
|
7
|
+
class SearchBar < Plutonium::UI::Component::Base
|
8
|
+
def view_template
|
9
|
+
original_attributes = Phlex::HTML::EVENT_ATTRIBUTES
|
10
|
+
temp_attributes = Phlex::HTML::EVENT_ATTRIBUTES.dup
|
11
|
+
temp_attributes.delete("oninput")
|
12
|
+
temp_attributes.delete("onclick")
|
13
|
+
Phlex::HTML.const_set(:EVENT_ATTRIBUTES, temp_attributes)
|
14
|
+
|
15
|
+
div(
|
16
|
+
class:
|
17
|
+
# "p-4 bg-white border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700 space-y-2 mb-4"
|
18
|
+
"space-y-2 mb-4"
|
19
|
+
) do
|
20
|
+
search_query = current_query_object.search_query
|
21
|
+
query_params = resource_query_params
|
22
|
+
render Phlexi::Form(:q, attributes: {data: {controller: "form", turbo_frame: nil}}) {
|
23
|
+
div(class: "relative") do
|
24
|
+
div(class: "absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none") do
|
25
|
+
svg(
|
26
|
+
class: "w-5 h-5 text-gray-500 dark:text-gray-400",
|
27
|
+
aria_hidden: "true",
|
28
|
+
fill: "currentColor",
|
29
|
+
viewbox: "0 0 20 20",
|
30
|
+
xmlns: "http://www.w3.org/2000/svg"
|
31
|
+
) do |s|
|
32
|
+
s.path(
|
33
|
+
fill_rule: "evenodd",
|
34
|
+
d:
|
35
|
+
"M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z",
|
36
|
+
clip_rule: "evenodd"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
render field(:search, value: search_query)
|
41
|
+
.placeholder("Search...")
|
42
|
+
.input_tag(
|
43
|
+
id: "search",
|
44
|
+
value: search_query,
|
45
|
+
class: "block w-full p-2 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500",
|
46
|
+
data: {
|
47
|
+
action: "form#submit",
|
48
|
+
turbo_permanent: true
|
49
|
+
}
|
50
|
+
)
|
51
|
+
|
52
|
+
render field(:scope, value: query_params[:scope]).input_tag(type: :hidden)
|
53
|
+
render field(:sort_fields, value: query_params[:sort_fields]).input_array_tag do |f|
|
54
|
+
render f.input_tag(type: :hidden)
|
55
|
+
end
|
56
|
+
nest_one(:sort_directions) do |directions|
|
57
|
+
query_params[:sort_directions]&.each do |name, value|
|
58
|
+
render directions.field(name, value:).input_tag(hidden: true)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
# div(class: "flex flex-wrap items-center gap-4") do
|
65
|
+
# span(class: "text-sm font-medium text-gray-900 dark:text-white") do
|
66
|
+
# "Filters:"
|
67
|
+
# end
|
68
|
+
# select(
|
69
|
+
# id: "category-filter",
|
70
|
+
# class:
|
71
|
+
# "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
72
|
+
# ) do
|
73
|
+
# option(selected: "selected", value: "") { "All Categories" }
|
74
|
+
# option(value: "technology") { "Technology" }
|
75
|
+
# option(value: "science") { "Science" }
|
76
|
+
# option(value: "health") { "Health" }
|
77
|
+
# end
|
78
|
+
# div(class: "flex items-center space-x-2") do
|
79
|
+
# input(
|
80
|
+
# type: "date",
|
81
|
+
# id: "start-date",
|
82
|
+
# class:
|
83
|
+
# "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
84
|
+
# )
|
85
|
+
# span(class: "text-gray-500 dark:text-gray-400") { "to" }
|
86
|
+
# input(
|
87
|
+
# type: "date",
|
88
|
+
# id: "end-date",
|
89
|
+
# class:
|
90
|
+
# "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
91
|
+
# )
|
92
|
+
# end
|
93
|
+
# select(
|
94
|
+
# id: "author-filter",
|
95
|
+
# class:
|
96
|
+
# "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
97
|
+
# ) do
|
98
|
+
# option(selected: "selected", value: "") { "All Authors" }
|
99
|
+
# option(value: "john-doe") { "John Doe" }
|
100
|
+
# option(value: "jane-smith") { "Jane Smith" }
|
101
|
+
# end
|
102
|
+
# button(
|
103
|
+
# onclick: "applyFilters()",
|
104
|
+
# class:
|
105
|
+
# "inline-flex items-center text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800"
|
106
|
+
# ) do
|
107
|
+
# svg(
|
108
|
+
# class: "w-4 h-4 mr-2",
|
109
|
+
# fill: "currentColor",
|
110
|
+
# viewbox: "0 0 20 20",
|
111
|
+
# xmlns: "http://www.w3.org/2000/svg"
|
112
|
+
# ) do |s|
|
113
|
+
# s.path(
|
114
|
+
# fill_rule: "evenodd",
|
115
|
+
# d:
|
116
|
+
# "M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z",
|
117
|
+
# clip_rule: "evenodd"
|
118
|
+
# )
|
119
|
+
# end
|
120
|
+
# plain " Apply Filters "
|
121
|
+
# end
|
122
|
+
# button(
|
123
|
+
# onclick: "clearFilters()",
|
124
|
+
# class:
|
125
|
+
# "inline-flex items-center text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-4 py-2 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"
|
126
|
+
# ) do
|
127
|
+
# svg(
|
128
|
+
# class: "w-4 h-4 mr-2",
|
129
|
+
# fill: "currentColor",
|
130
|
+
# viewbox: "0 0 20 20",
|
131
|
+
# xmlns: "http://www.w3.org/2000/svg"
|
132
|
+
# ) do |s|
|
133
|
+
# s.path(
|
134
|
+
# fill_rule: "evenodd",
|
135
|
+
# d:
|
136
|
+
# "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
|
137
|
+
# clip_rule: "evenodd"
|
138
|
+
# )
|
139
|
+
# end
|
140
|
+
# plain " Clear Filters "
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
end
|
144
|
+
ensure
|
145
|
+
# TODO: remove this once Phlex adds support for SafeValues
|
146
|
+
Phlex::HTML.const_set(:EVENT_ATTRIBUTES, original_attributes)
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def render?
|
152
|
+
current_query_object.search_filter.present? && current_policy.allowed_to?(:search?)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Table
|
6
|
+
class DisplayTheme < Phlexi::Table::DisplayTheme
|
7
|
+
def self.theme
|
8
|
+
super.merge({
|
9
|
+
string: "max-h-[150px] overflow-y-auto",
|
10
|
+
prefixed_icon: "w-4 h-4 mr-1",
|
11
|
+
link: "text-primary-600 dark:text-primary-500",
|
12
|
+
color: "flex items-center",
|
13
|
+
color_indicator: "w-10 h-10 rounded-full mr-2",
|
14
|
+
email: "flex items-center text-primary-600 dark:text-primary-500 whitespace-nowrap",
|
15
|
+
json: "max-h-[150px] overflow-y-auto whitespace-pre font-mono shadow-inner p-4"
|
16
|
+
})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Table
|
6
|
+
class Resource < Plutonium::UI::Component::Base
|
7
|
+
attr_reader :collection, :resource_fields, :resource_definition
|
8
|
+
|
9
|
+
def initialize(collection, resource_fields:, resource_definition:)
|
10
|
+
@collection = collection
|
11
|
+
@resource_fields = resource_fields
|
12
|
+
@resource_definition = resource_definition
|
13
|
+
end
|
14
|
+
|
15
|
+
def view_template
|
16
|
+
render_search_bar
|
17
|
+
render_scopes_bar
|
18
|
+
|
19
|
+
collection.empty? ? render_empty_card : render_table
|
20
|
+
|
21
|
+
render_footer
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def render_search_bar
|
27
|
+
TableSearchBar()
|
28
|
+
end
|
29
|
+
|
30
|
+
def render_scopes_bar
|
31
|
+
TableScopesBar()
|
32
|
+
end
|
33
|
+
|
34
|
+
def render_empty_card
|
35
|
+
EmptyCard("No #{resource_name_plural(resource_class)} match your query") {
|
36
|
+
action = resource_definition.defined_actions[:new]
|
37
|
+
if action&.permitted_by?(current_policy)
|
38
|
+
url = resource_url_for(resource_class, *action.route_options.url_args, **action.route_options.url_options)
|
39
|
+
ActionButton(action, url:)
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def render_table
|
45
|
+
render Plutonium::UI::Table::Base.new(collection) do |table|
|
46
|
+
@resource_fields.each do |name|
|
47
|
+
# column :name, as: :string
|
48
|
+
# column :description, class: "text-red-700"
|
49
|
+
# column :age, align: :end
|
50
|
+
# column :dob do |proxy|
|
51
|
+
# proxy.field(:dob).date_tag
|
52
|
+
# end
|
53
|
+
|
54
|
+
column_definition = resource_definition.defined_columns[name] || {}
|
55
|
+
column_display_options = column_definition[:options] || {}
|
56
|
+
display_field_as = column_display_options.delete(:as)
|
57
|
+
align_field_to = column_display_options.delete(:align)
|
58
|
+
|
59
|
+
display_block = column_definition[:block] || ->(wrapped_object, key) {
|
60
|
+
f = wrapped_object.field(key)
|
61
|
+
display_field_as ||= f.inferred_field_component
|
62
|
+
f.send(:"#{display_field_as}_tag", **column_display_options)
|
63
|
+
}
|
64
|
+
|
65
|
+
field_options = resource_definition.defined_fields[name] ? resource_definition.defined_fields[name][:options] : {}
|
66
|
+
field_options[:as] = display_field_as
|
67
|
+
field_options[:align] = align_field_to if align_field_to
|
68
|
+
table.column name, **field_options, sort_params: current_query_object.sort_params_for(name), &display_block
|
69
|
+
end
|
70
|
+
|
71
|
+
table.actions do |wrapped_object|
|
72
|
+
record = wrapped_object.unwrapped
|
73
|
+
policy = policy_for(record:)
|
74
|
+
|
75
|
+
div(class: "flex space-x-2") {
|
76
|
+
resource_definition.defined_actions
|
77
|
+
.select { |k, a| a.collection_record_action? && policy.allowed_to?(:"#{k}?") }
|
78
|
+
.values
|
79
|
+
.each { |action|
|
80
|
+
url = resource_url_for(record, *action.route_options.url_args, **action.route_options.url_options)
|
81
|
+
|
82
|
+
ActionButton(action, url:, variant: :table)
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def render_footer
|
90
|
+
div(class: "sticky bottom-[-2px] p-4 pb-6 w-full z-50 bg-gray-50 dark:bg-gray-900") {
|
91
|
+
TableInfo(pagy_instance)
|
92
|
+
TablePagination(pagy_instance)
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|