plutonium 0.15.5 → 0.15.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.ico +0 -0
- data/app/assets/plutonium.js +25 -11
- data/app/assets/plutonium.js.map +2 -2
- data/app/assets/plutonium.min.js +4 -4
- data/app/assets/plutonium.min.js.map +3 -3
- data/app/assets/plutonium.png +0 -0
- data/app/views/layouts/rodauth.html.erb +2 -2
- data/docs/.vitepress/config.ts +61 -0
- data/docs/.vitepress/theme/custom.css +61 -0
- data/docs/.vitepress/theme/index.ts +4 -0
- data/docs/api-examples.md +49 -0
- data/docs/guide/getting-started/authorization.md +296 -0
- data/docs/guide/getting-started/core-concepts.md +432 -0
- data/docs/guide/getting-started/index.md +18 -0
- data/docs/guide/getting-started/installation.md +270 -0
- data/docs/guide/getting-started/resources.md +250 -0
- data/docs/guide/what-is-plutonium.md +211 -0
- data/docs/index.md +43 -0
- data/docs/markdown-examples.md +85 -0
- data/docs/public/android-chrome-192x192.png +0 -0
- data/docs/public/android-chrome-512x512.png +0 -0
- data/docs/public/apple-touch-icon.png +0 -0
- data/docs/public/favicon-16x16.png +0 -0
- data/docs/public/favicon-32x32.png +0 -0
- data/docs/public/favicon.ico +0 -0
- data/docs/public/plutonium.png +0 -0
- data/docs/public/site.webmanifest +1 -0
- data/docs/public/templates/plutonium.rb +29 -0
- data/lib/generators/pu/core/assets/assets_generator.rb +2 -3
- data/lib/generators/pu/core/assets/templates/tailwind.config.js +2 -2
- data/lib/generators/pu/core/install/install_generator.rb +9 -1
- data/lib/generators/pu/core/install/templates/config/initializers/plutonium.rb +0 -1
- data/lib/generators/pu/eject/layout/layout_generator.rb +3 -3
- data/lib/generators/pu/eject/shell/shell_generator.rb +3 -3
- data/lib/generators/pu/gem/dotenv/dotenv_generator.rb +1 -1
- data/lib/generators/pu/gem/letter_opener/letter_opener_generator.rb +21 -0
- data/lib/generators/pu/gem/redis/redis_generator.rb +0 -2
- data/lib/generators/pu/gem/standard/standard_generator.rb +19 -0
- data/lib/generators/pu/lib/plutonium_generators/generator.rb +1 -1
- data/lib/generators/pu/res/conn/conn_generator.rb +1 -1
- data/lib/plutonium/core/controllers/authorizable.rb +1 -1
- data/lib/plutonium/definition/actions.rb +6 -2
- data/lib/plutonium/definition/base.rb +1 -0
- data/lib/plutonium/definition/nested_inputs.rb +19 -0
- data/lib/plutonium/railtie.rb +0 -10
- data/lib/plutonium/resource/controller.rb +1 -1
- data/lib/plutonium/resource/controllers/crud_actions.rb +1 -1
- data/lib/plutonium/resource/controllers/interactive_actions.rb +1 -1
- data/lib/plutonium/resource/controllers/presentable.rb +1 -5
- data/lib/plutonium/resource/policy.rb +4 -5
- data/lib/plutonium/resource/register.rb +3 -0
- data/lib/plutonium/ui/action_button.rb +34 -19
- data/lib/plutonium/ui/block.rb +13 -0
- data/lib/plutonium/ui/component/kit.rb +10 -0
- data/lib/plutonium/ui/display/resource.rb +29 -11
- data/lib/plutonium/ui/display/theme.rb +1 -1
- data/lib/plutonium/ui/dyna_frame/content.rb +2 -2
- data/lib/plutonium/ui/dyna_frame/host.rb +20 -0
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +282 -0
- data/lib/plutonium/ui/form/resource.rb +40 -29
- data/lib/plutonium/ui/form/theme.rb +1 -1
- data/lib/plutonium/ui/frame_navigator_panel.rb +53 -0
- data/lib/plutonium/ui/panel.rb +63 -0
- data/lib/plutonium/ui/skeleton_table.rb +29 -0
- data/lib/plutonium/ui/table/components/search_bar.rb +1 -1
- data/lib/plutonium/ui/table/resource.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package-lock.json +5767 -1851
- data/package.json +10 -4
- data/src/js/controllers/frame_navigator_controller.js +25 -8
- data/src/js/controllers/nested_resource_form_fields_controller.js +2 -2
- data/src/js/core.js +0 -1
- data/tailwind.options.js +89 -11
- metadata +36 -12
- data/app/assets/plutonium-original.png +0 -0
- data/app/assets/plutonium-white.png +0 -0
- data/lib/generators/pu/gem/redis/templates/.keep +0 -0
- data/public/plutonium-assets/fonts/bootstrap-icons.woff +0 -0
- data/public/plutonium-assets/fonts/bootstrap-icons.woff2 +0 -0
- /data/{templates → docs/public/templates}/base.rb +0 -0
@@ -0,0 +1,282 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plutonium
|
4
|
+
module UI
|
5
|
+
module Form
|
6
|
+
module Concerns
|
7
|
+
# Handles rendering of nested resource fields in forms
|
8
|
+
# @api private
|
9
|
+
module RendersNestedResourceFields
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
DEFAULT_NESTED_LIMIT = 10
|
13
|
+
NESTED_OPTION_KEYS = [:allow_destroy, :update_only, :macro, :class].freeze
|
14
|
+
SINGULAR_MACROS = %i[belongs_to has_one].freeze
|
15
|
+
|
16
|
+
class NestedInputsDefinition
|
17
|
+
include Plutonium::Definition::DefineableProps
|
18
|
+
|
19
|
+
defineable_props :field, :input
|
20
|
+
end
|
21
|
+
|
22
|
+
# Template object for new nested records
|
23
|
+
class NotPersisted
|
24
|
+
def persisted?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Renders a nested resource field with associated inputs
|
32
|
+
# @param [Symbol] name The name of the nested resource field
|
33
|
+
# @raise [ArgumentError] if the nested input definition is missing required configuration
|
34
|
+
def render_nested_resource_field(name)
|
35
|
+
context = NestedFieldContext.new(
|
36
|
+
name: name,
|
37
|
+
definition: build_nested_definition(name),
|
38
|
+
resource_class: resource_class,
|
39
|
+
resource_definition: resource_definition
|
40
|
+
)
|
41
|
+
|
42
|
+
render_nested_field_container(context) do
|
43
|
+
render_nested_field_header(context)
|
44
|
+
render_nested_field_content(context)
|
45
|
+
render_nested_add_button(context)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
class NestedFieldContext
|
52
|
+
attr_reader :name, :definition, :options, :permitted_fields
|
53
|
+
|
54
|
+
def initialize(name:, definition:, resource_class:, resource_definition:)
|
55
|
+
@name = name
|
56
|
+
@definition = definition
|
57
|
+
@resource_definition = resource_definition
|
58
|
+
@resource_class = resource_class
|
59
|
+
@options = build_options
|
60
|
+
@permitted_fields = build_permitted_fields
|
61
|
+
end
|
62
|
+
|
63
|
+
def nested_attribute_options
|
64
|
+
@nested_attribute_options ||= @resource_class.all_nested_attributes_options[@name] || {}
|
65
|
+
end
|
66
|
+
|
67
|
+
def nested_input_param
|
68
|
+
@options[:as] || :"#{@name}_attributes"
|
69
|
+
end
|
70
|
+
|
71
|
+
def multiple?
|
72
|
+
@options[:multiple]
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def build_options
|
78
|
+
options = @resource_definition.defined_nested_inputs[@name][:options].dup || {}
|
79
|
+
merge_nested_options(options)
|
80
|
+
set_nested_limits(options)
|
81
|
+
options
|
82
|
+
end
|
83
|
+
|
84
|
+
def merge_nested_options(options)
|
85
|
+
NESTED_OPTION_KEYS.each do |key|
|
86
|
+
options.fetch(key) { options[key] = nested_attribute_options[key] }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_nested_limits(options)
|
91
|
+
options.fetch(:limit) do
|
92
|
+
options[:limit] = if SINGULAR_MACROS.include?(nested_attribute_options[:macro])
|
93
|
+
1
|
94
|
+
else
|
95
|
+
nested_attribute_options[:limit] || DEFAULT_NESTED_LIMIT
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
options.fetch(:multiple) do
|
100
|
+
options[:multiple] = !SINGULAR_MACROS.include?(nested_attribute_options[:macro])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def build_permitted_fields
|
105
|
+
@options[:fields] || @definition.defined_inputs.keys
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_nested_definition(name)
|
110
|
+
nested_input_definition = resource_definition.defined_nested_inputs[name]
|
111
|
+
|
112
|
+
if nested_input_definition[:options]&.fetch(:using, nil)
|
113
|
+
nested_input_definition[:options][:using]
|
114
|
+
elsif nested_input_definition[:block]
|
115
|
+
build_definition_from_block(nested_input_definition[:block])
|
116
|
+
else
|
117
|
+
raise_missing_nested_definition_error(name)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_definition_from_block(block)
|
122
|
+
definition = NestedInputsDefinition.new
|
123
|
+
block.call(definition)
|
124
|
+
definition
|
125
|
+
end
|
126
|
+
|
127
|
+
def render_nested_field_container(context, &)
|
128
|
+
div(
|
129
|
+
class: "col-span-full space-y-2 my-4",
|
130
|
+
data: {
|
131
|
+
controller: "nested-resource-form-fields",
|
132
|
+
nested_resource_form_fields_limit_value: context.options[:limit]
|
133
|
+
},
|
134
|
+
&
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
def render_nested_field_header(context)
|
139
|
+
div do
|
140
|
+
h2(class: "text-lg font-semibold text-gray-900 dark:text-white") { context.name.to_s.humanize }
|
141
|
+
render_description(context.options[:description]) if context.options[:description]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def render_description(description)
|
146
|
+
p(class: "text-md font-normal text-gray-500 dark:text-gray-400") { description }
|
147
|
+
end
|
148
|
+
|
149
|
+
def render_nested_field_content(context)
|
150
|
+
if context.multiple?
|
151
|
+
render_multiple_nested_fields(context)
|
152
|
+
else
|
153
|
+
render_single_nested_field(context)
|
154
|
+
end
|
155
|
+
|
156
|
+
div(data_nested_resource_form_fields_target: :target, hidden: true)
|
157
|
+
end
|
158
|
+
|
159
|
+
def render_multiple_nested_fields(context)
|
160
|
+
render_template_for_nested_fields(context, collection: {NEW_RECORD: NotPersisted.new})
|
161
|
+
render_existing_nested_fields(context)
|
162
|
+
end
|
163
|
+
|
164
|
+
def render_single_nested_field(context)
|
165
|
+
render_template_for_nested_fields(context, object: NotPersisted.new)
|
166
|
+
render_existing_nested_fields(context, single: true)
|
167
|
+
end
|
168
|
+
|
169
|
+
def render_template_for_nested_fields(context, field_options)
|
170
|
+
template_tag data_nested_resource_form_fields_target: "template" do
|
171
|
+
nesting_method = field_options[:collection] ? :nest_many : :nest_one
|
172
|
+
send(nesting_method, context.name, as: context.nested_input_param, template: true, **field_options) do |nested|
|
173
|
+
render_fieldset(nested, context)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def render_existing_nested_fields(context, single: false)
|
179
|
+
nesting_method = single ? :nest_one : :nest_many
|
180
|
+
send(nesting_method, context.name, as: context.nested_input_param) do |nested|
|
181
|
+
render_fieldset(nested, context)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def render_fieldset(nested, context)
|
186
|
+
fieldset(
|
187
|
+
data_new_record: !nested.object&.persisted?,
|
188
|
+
class: "nested-resource-form-fields border border-gray-200 dark:border-gray-700 rounded-lg p-4 space-y-4 relative"
|
189
|
+
) do
|
190
|
+
render_fieldset_content(nested, context)
|
191
|
+
render_delete_button(nested, context.options)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def render_fieldset_content(nested, context)
|
196
|
+
div(class: "grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-4 grid-flow-row-dense") do
|
197
|
+
render_hidden_fields(nested, context)
|
198
|
+
render_input_fields(nested, context)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def render_hidden_fields(nested, context)
|
203
|
+
if !context.options[:update_only] && context.options[:class]&.respond_to?(:primary_key)
|
204
|
+
render nested.field(context.options[:class].primary_key).hidden_tag
|
205
|
+
end
|
206
|
+
render nested.field(:_destroy).hidden_tag if context.options[:allow_destroy]
|
207
|
+
end
|
208
|
+
|
209
|
+
def render_input_fields(nested, context)
|
210
|
+
context.permitted_fields.each do |input|
|
211
|
+
render_simple_resource_field(input, context.definition, nested)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def render_delete_button(nested, options)
|
216
|
+
return unless !nested.object&.persisted? || options[:allow_destroy]
|
217
|
+
|
218
|
+
render_delete_button_content
|
219
|
+
end
|
220
|
+
|
221
|
+
def render_delete_button_content
|
222
|
+
div(class: "flex items-center justify-end") do
|
223
|
+
label(class: "inline-flex items-center text-md font-medium text-red-900 cursor-pointer") do
|
224
|
+
plain "Delete"
|
225
|
+
render_delete_checkbox
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def render_delete_checkbox
|
231
|
+
input(
|
232
|
+
type: :checkbox,
|
233
|
+
class: "w-4 h-4 ms-2 text-red-600 bg-red-100 border-red-300 rounded focus:ring-red-500 dark:focus:ring-red-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 cursor-pointer",
|
234
|
+
data_action: "nested-resource-form-fields#remove"
|
235
|
+
)
|
236
|
+
end
|
237
|
+
|
238
|
+
def render_nested_add_button(context)
|
239
|
+
div do
|
240
|
+
button(
|
241
|
+
type: :button,
|
242
|
+
class: "inline-block",
|
243
|
+
data: {
|
244
|
+
action: "nested-resource-form-fields#add",
|
245
|
+
nested_resource_form_fields_target: "addButton"
|
246
|
+
}
|
247
|
+
) do
|
248
|
+
render_add_button_content(context.name)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def render_add_button_content(name)
|
254
|
+
span(class: "bg-secondary-700 text-white hover:bg-secondary-800 focus:ring-secondary-300 dark:bg-secondary-600 dark:hover:bg-secondary-700 dark:focus:ring-secondary-800 flex items-center justify-center px-4 py-1.5 text-sm font-medium rounded-lg focus:outline-none focus:ring-4") do
|
255
|
+
render Phlex::TablerIcons::Plus.new(class: "w-4 h-4 mr-1")
|
256
|
+
span { "Add #{name.to_s.singularize.humanize}" }
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def raise_missing_nested_definition_error(name)
|
261
|
+
raise ArgumentError, %(
|
262
|
+
`nested_input :#{name}` is missing a definition
|
263
|
+
|
264
|
+
you can either pass in a block:
|
265
|
+
```ruby
|
266
|
+
nested_input :#{name} do |definition|
|
267
|
+
input :city
|
268
|
+
input :country
|
269
|
+
end
|
270
|
+
```
|
271
|
+
|
272
|
+
or pass in options:
|
273
|
+
```ruby
|
274
|
+
nested_input :#{name}, using: #{name.to_s.classify}Definition, fields: %i[city country]
|
275
|
+
```
|
276
|
+
)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
@@ -4,6 +4,8 @@ module Plutonium
|
|
4
4
|
module UI
|
5
5
|
module Form
|
6
6
|
class Resource < Base
|
7
|
+
include Plutonium::UI::Form::Concerns::RendersNestedResourceFields
|
8
|
+
|
7
9
|
attr_reader :resource_fields, :resource_definition
|
8
10
|
|
9
11
|
def initialize(*, resource_fields:, resource_definition:, **, &)
|
@@ -35,39 +37,48 @@ module Plutonium
|
|
35
37
|
|
36
38
|
def render_resource_field(name)
|
37
39
|
when_permitted(name) do
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# input :dob do |f|
|
43
|
-
# f.date_tag
|
44
|
-
# end
|
45
|
-
|
46
|
-
field_options = resource_definition.defined_fields[name] ? resource_definition.defined_fields[name][:options] : {}
|
47
|
-
|
48
|
-
input_definition = resource_definition.defined_inputs[name] || {}
|
49
|
-
input_options = input_definition[:options] || {}
|
50
|
-
|
51
|
-
tag = field_options[:as] || input_options[:as]
|
52
|
-
tag_attributes = input_options[:tag] || {}
|
53
|
-
tag_block = input_definition[:block] || ->(f) {
|
54
|
-
tag ||= f.inferred_field_component
|
55
|
-
f.send(:"#{tag}_tag", **tag_attributes)
|
56
|
-
}
|
57
|
-
|
58
|
-
field_options = field_options.except(:as)
|
59
|
-
wrapper_options = input_options.except(:tag, :as)
|
60
|
-
if !wrapper_options[:class] || wrapper_options[:class].include?("col-span")
|
61
|
-
# temp hack to allow col span overrides
|
62
|
-
# TODO: remove once we complete theming, which will support merges
|
63
|
-
wrapper_options[:class] = tokens("col-span-full", wrapper_options[:class])
|
64
|
-
end
|
65
|
-
render field(name, **field_options).wrapped(**wrapper_options) do |f|
|
66
|
-
render tag_block.call(f)
|
40
|
+
if resource_definition.defined_nested_inputs[name]
|
41
|
+
render_nested_resource_field(name)
|
42
|
+
else
|
43
|
+
render_simple_resource_field(name, resource_definition, self)
|
67
44
|
end
|
68
45
|
end
|
69
46
|
end
|
70
47
|
|
48
|
+
def render_simple_resource_field(name, definition, form)
|
49
|
+
# field :name, as: :string
|
50
|
+
# input :name, as: :string
|
51
|
+
# input :description, class: "col-span-full"
|
52
|
+
# input :age, tag: {class: "max-h-fit"}
|
53
|
+
# input :dob do |f|
|
54
|
+
# f.date_tag
|
55
|
+
# end
|
56
|
+
|
57
|
+
field_options = definition.defined_fields[name] ? definition.defined_fields[name][:options] : {}
|
58
|
+
|
59
|
+
input_definition = definition.defined_inputs[name] || {}
|
60
|
+
input_options = input_definition[:options] || {}
|
61
|
+
|
62
|
+
tag = field_options[:as] || input_options[:as]
|
63
|
+
tag_attributes = input_options[:tag] || {}
|
64
|
+
tag_block = input_definition[:block] || ->(f) {
|
65
|
+
tag ||= f.inferred_field_component
|
66
|
+
f.send(:"#{tag}_tag", **tag_attributes)
|
67
|
+
}
|
68
|
+
|
69
|
+
field_options = field_options.except(:as)
|
70
|
+
wrapper_options = input_options.except(:tag, :as)
|
71
|
+
if !wrapper_options[:class] || !wrapper_options[:class].include?("col-span")
|
72
|
+
# temp hack to allow col span overrides
|
73
|
+
# TODO: remove once we complete theming, which will support merges
|
74
|
+
wrapper_options[:class] = tokens("col-span-full", wrapper_options[:class])
|
75
|
+
end
|
76
|
+
|
77
|
+
render form.field(name, **field_options).wrapped(**wrapper_options) do |f|
|
78
|
+
render tag_block.call(f)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
71
82
|
def when_permitted(name, &)
|
72
83
|
return unless resource_fields.include? name
|
73
84
|
|
@@ -7,7 +7,7 @@ module Plutonium
|
|
7
7
|
def self.theme
|
8
8
|
super.merge({
|
9
9
|
base: "relative bg-white dark:bg-gray-800 shadow-md sm:rounded-lg my-3 p-6 space-y-6",
|
10
|
-
fields_wrapper: "grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-
|
10
|
+
fields_wrapper: "grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-4 grid-flow-row-dense",
|
11
11
|
actions_wrapper: "flex justify-end space-x-2",
|
12
12
|
wrapper: nil,
|
13
13
|
inner_wrapper: "w-full",
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Plutonium
|
2
|
+
module UI
|
3
|
+
class FrameNavigatorPanel < Plutonium::UI::Component::Base
|
4
|
+
class PanelItem < Plutonium::UI::Component::Base
|
5
|
+
def initialize(label:, icon:, **attributes)
|
6
|
+
@label = label
|
7
|
+
@icon = icon
|
8
|
+
@attributes = attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def view_template
|
12
|
+
button(
|
13
|
+
title: @label,
|
14
|
+
style: "display: none",
|
15
|
+
class: "text-gray-600 dark:text-gray-300",
|
16
|
+
**@attributes
|
17
|
+
) {
|
18
|
+
render @icon.new(class: "w-6 h-6")
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class PanelContent < Plutonium::UI::Component::Base
|
24
|
+
def initialize(src:)
|
25
|
+
@src = src
|
26
|
+
end
|
27
|
+
|
28
|
+
def view_template
|
29
|
+
DynaFrameHost src: @src, loading: :lazy, data: {"frame-navigator-target": "frame"} do
|
30
|
+
SkeletonTable()
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(title:, src:)
|
36
|
+
@title = title
|
37
|
+
@src = src
|
38
|
+
end
|
39
|
+
|
40
|
+
def view_template
|
41
|
+
div(data: {controller: %w[has-many-panel frame-navigator]}) do
|
42
|
+
Panel do |panel|
|
43
|
+
panel.with_title @title
|
44
|
+
panel.with_item PanelItem.new(label: "Home", icon: Phlex::TablerIcons::Home2, data_frame_navigator_target: %(homeButton))
|
45
|
+
panel.with_item PanelItem.new(label: "Back", icon: Phlex::TablerIcons::ChevronLeft, data_frame_navigator_target: %(backButton))
|
46
|
+
panel.with_item PanelItem.new(label: "Refresh", icon: Phlex::TablerIcons::RefreshDot, data_frame_navigator_target: %(refreshButton))
|
47
|
+
panel.with_content PanelContent.new(src: @src)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Plutonium
|
2
|
+
module UI
|
3
|
+
class Panel < Plutonium::UI::Component::Base
|
4
|
+
include Phlex::DeferredRender
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@items = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def with_title(title)
|
11
|
+
@title = title
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_item(item)
|
15
|
+
@items << item
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_content(content)
|
19
|
+
@content = content
|
20
|
+
end
|
21
|
+
|
22
|
+
def view_template
|
23
|
+
wrapped do
|
24
|
+
render_toolbar if render_toolbar?
|
25
|
+
render_content if render_content?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def wrapped(&)
|
32
|
+
div(class: "mt-6", &)
|
33
|
+
end
|
34
|
+
|
35
|
+
def render_toolbar
|
36
|
+
div(class: %(flex justify-between items-center mb-4)) do
|
37
|
+
if @title.present?
|
38
|
+
h5(class: %(text-2xl font-bold tracking-tight text-gray-900 dark:text-white)) do
|
39
|
+
@title
|
40
|
+
end
|
41
|
+
end
|
42
|
+
div(class: "flex space-x-4") do
|
43
|
+
@items.each do |item|
|
44
|
+
render item
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def render_content
|
51
|
+
render @content
|
52
|
+
end
|
53
|
+
|
54
|
+
def render_toolbar?
|
55
|
+
@title || @items.present?
|
56
|
+
end
|
57
|
+
|
58
|
+
def render_content?
|
59
|
+
@content
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Plutonium
|
2
|
+
module UI
|
3
|
+
class SkeletonTable < Plutonium::UI::Component::Base
|
4
|
+
def view_template
|
5
|
+
div(
|
6
|
+
role: "status",
|
7
|
+
class:
|
8
|
+
"p-4 space-y-4 border border-gray-200 divide-y divide-gray-200 rounded shadow motion-safe:animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700"
|
9
|
+
) do
|
10
|
+
div(class: "flex items-center justify-between") do
|
11
|
+
div do
|
12
|
+
div(class: "h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5")
|
13
|
+
div(class: "w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700")
|
14
|
+
end
|
15
|
+
div(class: "h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12")
|
16
|
+
end
|
17
|
+
div(class: "flex items-center justify-between pt-4") do
|
18
|
+
div do
|
19
|
+
div(class: "h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5")
|
20
|
+
div(class: "w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700")
|
21
|
+
end
|
22
|
+
div(class: "h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12")
|
23
|
+
end
|
24
|
+
span(class: "sr-only") { "Loading..." }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -16,7 +16,7 @@ module Plutonium
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def render?
|
19
|
-
current_query_object.filter_definitions.present? && current_policy.allowed_to?(:search?)
|
19
|
+
(current_query_object.search_filter.present? || current_query_object.filter_definitions.present?) && current_policy.allowed_to?(:search?)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -94,7 +94,7 @@ module Plutonium
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def render_footer
|
97
|
-
div(class: "sticky bottom-[-2px] p-4 pb-6 w-full z-50 bg-gray-50 dark:bg-gray-900") {
|
97
|
+
div(class: "sticky bottom-[-2px] mt-1 p-4 pb-6 w-full z-50 bg-gray-50 dark:bg-gray-900") {
|
98
98
|
TableInfo(pagy_instance)
|
99
99
|
TablePagination(pagy_instance)
|
100
100
|
}
|
data/lib/plutonium/version.rb
CHANGED