plutonium 0.15.5 → 0.15.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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