plutonium 0.15.7 → 0.15.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +19 -81
- data/app/assets/plutonium.css +1 -1
- data/docs/.vitepress/config.ts +1 -0
- data/docs/guide/getting-started/index.md +4 -1
- data/docs/guide/tutorial.md +403 -0
- data/docs/index.md +19 -15
- data/docs/public/tutorial/plutonium-association-panel.png +0 -0
- data/docs/public/tutorial/plutonium-dashboard.png +0 -0
- data/docs/public/tutorial/plutonium-login-page.png +0 -0
- data/docs/public/tutorial/plutonium-nested-form.png +0 -0
- data/docs/public/tutorial/plutonium-posts-dashboard-customized.png +0 -0
- data/docs/public/tutorial/plutonium-posts-dashboard.png +0 -0
- data/docs/public/tutorial/plutonium-posts-detail-customized.png +0 -0
- data/docs/public/tutorial/plutonium-posts-detail.png +0 -0
- data/docs/public/tutorial/plutonium-publish-post.png +0 -0
- data/lib/generators/pu/core/assets/templates/tailwind.config.js +11 -1
- data/lib/generators/pu/extra/colorized_logger/colorized_logger_generator.rb +21 -0
- data/lib/generators/pu/extra/colorized_logger/templates/config/initializers/colorized_logger.rb +22 -0
- data/lib/generators/pu/gem/standard/standard_generator.rb +1 -1
- data/lib/plutonium/core/controllers/authorizable.rb +1 -1
- data/lib/plutonium/resource/controller.rb +12 -8
- data/lib/plutonium/resource/controllers/authorizable.rb +2 -2
- data/lib/plutonium/resource/controllers/presentable.rb +15 -4
- data/lib/plutonium/ui/dyna_frame/host.rb +1 -1
- data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +73 -70
- data/lib/plutonium/ui/table/resource.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package-lock.json +2 -2
- data/package.json +1 -1
- data/tailwind.config.js +11 -1
- data/tailwind.options.js +7 -1
- metadata +14 -2
@@ -5,7 +5,6 @@ module Plutonium
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
helper_method :presentable_attributes
|
9
8
|
helper_method :build_form, :build_detail, :build_collection
|
10
9
|
end
|
11
10
|
|
@@ -14,12 +13,24 @@ module Plutonium
|
|
14
13
|
def presentable_attributes
|
15
14
|
@presentable_attributes ||= begin
|
16
15
|
presentable_attributes = permitted_attributes
|
17
|
-
|
18
|
-
|
16
|
+
if current_parent
|
17
|
+
presentable_attributes -= [parent_input_param, :"#{parent_input_param}_id"]
|
18
|
+
elsif scoped_to_entity?
|
19
|
+
presentable_attributes -= [scoped_entity_param_key, :"#{scoped_entity_param_key}_id"]
|
20
|
+
end
|
19
21
|
presentable_attributes
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
25
|
+
def submittable_attributes
|
26
|
+
@submittable_attributes ||= begin
|
27
|
+
submittable_attributes = permitted_attributes
|
28
|
+
submittable_attributes -= [parent_input_param, :"#{parent_input_param}_id"] if current_parent
|
29
|
+
submittable_attributes -= [scoped_entity_param_key, :"#{scoped_entity_param_key}_id"] if scoped_to_entity?
|
30
|
+
submittable_attributes
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
23
34
|
def build_collection
|
24
35
|
current_definition.collection_class.new(@resource_records, resource_fields: presentable_attributes, resource_definition: current_definition)
|
25
36
|
end
|
@@ -29,7 +40,7 @@ module Plutonium
|
|
29
40
|
end
|
30
41
|
|
31
42
|
def build_form(record = resource_record)
|
32
|
-
current_definition.form_class.new(record, resource_fields:
|
43
|
+
current_definition.form_class.new(record, resource_fields: submittable_attributes, resource_definition: current_definition)
|
33
44
|
end
|
34
45
|
end
|
35
46
|
end
|
@@ -5,6 +5,7 @@ module Plutonium
|
|
5
5
|
module Form
|
6
6
|
module Concerns
|
7
7
|
# Handles rendering of nested resource fields in forms
|
8
|
+
# TODO: further decompose this into components
|
8
9
|
# @api private
|
9
10
|
module RendersNestedResourceFields
|
10
11
|
extend ActiveSupport::Concern
|
@@ -19,75 +20,51 @@ module Plutonium
|
|
19
20
|
defineable_props :field, :input
|
20
21
|
end
|
21
22
|
|
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
23
|
class NestedFieldContext
|
52
24
|
attr_reader :name, :definition, :options, :permitted_fields
|
53
25
|
|
54
|
-
def initialize(name:, definition:, resource_class:, resource_definition:)
|
26
|
+
def initialize(name:, definition:, resource_class:, resource_definition:, object_class:)
|
55
27
|
@name = name
|
56
28
|
@definition = definition
|
57
29
|
@resource_definition = resource_definition
|
58
30
|
@resource_class = resource_class
|
59
31
|
@options = build_options
|
60
32
|
@permitted_fields = build_permitted_fields
|
33
|
+
@object_class = object_class
|
61
34
|
end
|
62
35
|
|
63
36
|
def nested_attribute_options
|
64
37
|
@nested_attribute_options ||= @resource_class.all_nested_attributes_options[@name] || {}
|
65
38
|
end
|
66
39
|
|
67
|
-
def
|
40
|
+
def nested_fields_input_param
|
68
41
|
@options[:as] || :"#{@name}_attributes"
|
69
42
|
end
|
70
43
|
|
71
|
-
def
|
44
|
+
def nested_fields_multiple?
|
72
45
|
@options[:multiple]
|
73
46
|
end
|
74
47
|
|
48
|
+
def blank_object
|
49
|
+
(@object_class || nested_attribute_options[:class])&.new
|
50
|
+
end
|
51
|
+
|
75
52
|
private
|
76
53
|
|
77
54
|
def build_options
|
78
55
|
options = @resource_definition.defined_nested_inputs[@name][:options].dup || {}
|
79
|
-
|
80
|
-
|
56
|
+
merge_nested_fields_options(options)
|
57
|
+
set_nested_fields_limits(options)
|
81
58
|
options
|
82
59
|
end
|
83
60
|
|
84
|
-
def
|
61
|
+
def merge_nested_fields_options(options)
|
85
62
|
NESTED_OPTION_KEYS.each do |key|
|
86
63
|
options.fetch(key) { options[key] = nested_attribute_options[key] }
|
87
64
|
end
|
88
65
|
end
|
89
66
|
|
90
|
-
def
|
67
|
+
def set_nested_fields_limits(options)
|
91
68
|
options.fetch(:limit) do
|
92
69
|
options[:limit] = if SINGULAR_MACROS.include?(nested_attribute_options[:macro])
|
93
70
|
1
|
@@ -106,19 +83,43 @@ module Plutonium
|
|
106
83
|
end
|
107
84
|
end
|
108
85
|
|
109
|
-
|
86
|
+
# Template object for new nested records
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Renders a nested resource field with associated inputs
|
91
|
+
# @param [Symbol] name The name of the nested resource field
|
92
|
+
# @raise [ArgumentError] if the nested input definition is missing required configuration
|
93
|
+
def render_nested_resource_field(name)
|
94
|
+
# debugger if $extracting_input
|
95
|
+
context = NestedFieldContext.new(
|
96
|
+
name: name,
|
97
|
+
definition: build_nested_fields_definition(name),
|
98
|
+
resource_class: resource_class,
|
99
|
+
resource_definition: resource_definition,
|
100
|
+
object_class: resource_definition.defined_nested_inputs[name][:options]&.fetch(:object_class, nil)
|
101
|
+
)
|
102
|
+
|
103
|
+
render_nested_field_container(context) do
|
104
|
+
render_nested_field_header(context)
|
105
|
+
render_nested_field_content(context)
|
106
|
+
render_nested_fields_add_button(context)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def build_nested_fields_definition(name)
|
110
111
|
nested_input_definition = resource_definition.defined_nested_inputs[name]
|
111
112
|
|
112
113
|
if nested_input_definition[:options]&.fetch(:using, nil)
|
113
114
|
nested_input_definition[:options][:using]
|
114
115
|
elsif nested_input_definition[:block]
|
115
|
-
|
116
|
+
build_nested_fields_definition_from_block(nested_input_definition[:block])
|
116
117
|
else
|
117
118
|
raise_missing_nested_definition_error(name)
|
118
119
|
end
|
119
120
|
end
|
120
121
|
|
121
|
-
def
|
122
|
+
def build_nested_fields_definition_from_block(block)
|
122
123
|
definition = NestedInputsDefinition.new
|
123
124
|
block.call(definition)
|
124
125
|
definition
|
@@ -138,16 +139,16 @@ module Plutonium
|
|
138
139
|
def render_nested_field_header(context)
|
139
140
|
div do
|
140
141
|
h2(class: "text-lg font-semibold text-gray-900 dark:text-white") { context.name.to_s.humanize }
|
141
|
-
|
142
|
+
render_nested_fields_header_description(context.options[:description]) if context.options[:description]
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
145
|
-
def
|
146
|
+
def render_nested_fields_header_description(description)
|
146
147
|
p(class: "text-md font-normal text-gray-500 dark:text-gray-400") { description }
|
147
148
|
end
|
148
149
|
|
149
150
|
def render_nested_field_content(context)
|
150
|
-
if context.
|
151
|
+
if context.nested_fields_multiple?
|
151
152
|
render_multiple_nested_fields(context)
|
152
153
|
else
|
153
154
|
render_single_nested_field(context)
|
@@ -157,77 +158,79 @@ module Plutonium
|
|
157
158
|
end
|
158
159
|
|
159
160
|
def render_multiple_nested_fields(context)
|
160
|
-
|
161
|
-
|
161
|
+
nesting_method = :nest_many
|
162
|
+
options = {default: {NEW_RECORD: context.blank_object}}
|
163
|
+
render_template_for_nested_fields(context, options, nesting_method:)
|
164
|
+
render_existing_nested_fields(context, options, nesting_method:)
|
162
165
|
end
|
163
166
|
|
164
167
|
def render_single_nested_field(context)
|
165
|
-
|
166
|
-
|
168
|
+
nesting_method = :nest_one
|
169
|
+
options = {default: context.blank_object}
|
170
|
+
render_template_for_nested_fields(context, options, nesting_method:)
|
171
|
+
render_existing_nested_fields(context, options, nesting_method:)
|
167
172
|
end
|
168
173
|
|
169
|
-
def render_template_for_nested_fields(context,
|
174
|
+
def render_template_for_nested_fields(context, options, nesting_method:)
|
170
175
|
template_tag data_nested_resource_form_fields_target: "template" do
|
171
|
-
nesting_method
|
172
|
-
|
173
|
-
render_fieldset(nested, context)
|
176
|
+
send(nesting_method, context.name, as: context.nested_fields_input_param, **options, template: true) do |nested|
|
177
|
+
render_nested_fields_fieldset(nested, context)
|
174
178
|
end
|
175
179
|
end
|
176
180
|
end
|
177
181
|
|
178
|
-
def render_existing_nested_fields(context,
|
179
|
-
nesting_method
|
180
|
-
|
181
|
-
render_fieldset(nested, context)
|
182
|
+
def render_existing_nested_fields(context, options, nesting_method:)
|
183
|
+
send(nesting_method, context.name, as: context.nested_fields_input_param, **options) do |nested|
|
184
|
+
render_nested_fields_fieldset(nested, context)
|
182
185
|
end
|
183
186
|
end
|
184
187
|
|
185
|
-
def
|
188
|
+
def render_nested_fields_fieldset(nested, context)
|
186
189
|
fieldset(
|
187
190
|
data_new_record: !nested.object&.persisted?,
|
188
191
|
class: "nested-resource-form-fields border border-gray-200 dark:border-gray-700 rounded-lg p-4 space-y-4 relative"
|
189
192
|
) do
|
190
|
-
|
191
|
-
|
193
|
+
render_nested_fields_fieldset_content(nested, context)
|
194
|
+
render_nested_fields_delete_button(nested, context.options)
|
192
195
|
end
|
193
196
|
end
|
194
197
|
|
195
|
-
def
|
198
|
+
def render_nested_fields_fieldset_content(nested, context)
|
196
199
|
div(class: "grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-4 gap-4 grid-flow-row-dense") do
|
197
|
-
|
198
|
-
|
200
|
+
render_nested_fields_hidden_fields(nested, context)
|
201
|
+
render_nested_fields_visible_fields(nested, context)
|
199
202
|
end
|
200
203
|
end
|
201
204
|
|
202
|
-
def
|
205
|
+
def render_nested_fields_hidden_fields(nested, context)
|
203
206
|
if !context.options[:update_only] && context.options[:class]&.respond_to?(:primary_key)
|
204
207
|
render nested.field(context.options[:class].primary_key).hidden_tag
|
205
208
|
end
|
206
209
|
render nested.field(:_destroy).hidden_tag if context.options[:allow_destroy]
|
207
210
|
end
|
208
211
|
|
209
|
-
def
|
212
|
+
def render_nested_fields_visible_fields(nested, context)
|
210
213
|
context.permitted_fields.each do |input|
|
211
214
|
render_simple_resource_field(input, context.definition, nested)
|
212
215
|
end
|
213
216
|
end
|
214
217
|
|
215
|
-
def
|
218
|
+
def render_nested_fields_delete_button(nested, options)
|
216
219
|
return unless !nested.object&.persisted? || options[:allow_destroy]
|
217
220
|
|
218
|
-
|
221
|
+
render_nested_fields_delete_button_content
|
219
222
|
end
|
220
223
|
|
221
|
-
def
|
224
|
+
def render_nested_fields_delete_button_content
|
222
225
|
div(class: "flex items-center justify-end") do
|
223
226
|
label(class: "inline-flex items-center text-md font-medium text-red-900 cursor-pointer") do
|
224
227
|
plain "Delete"
|
225
|
-
|
228
|
+
render_nested_fields_delete_checkbox
|
226
229
|
end
|
227
230
|
end
|
228
231
|
end
|
229
232
|
|
230
|
-
def
|
233
|
+
def render_nested_fields_delete_checkbox
|
231
234
|
input(
|
232
235
|
type: :checkbox,
|
233
236
|
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",
|
@@ -235,7 +238,7 @@ module Plutonium
|
|
235
238
|
)
|
236
239
|
end
|
237
240
|
|
238
|
-
def
|
241
|
+
def render_nested_fields_add_button(context)
|
239
242
|
div do
|
240
243
|
button(
|
241
244
|
type: :button,
|
@@ -245,12 +248,12 @@ module Plutonium
|
|
245
248
|
nested_resource_form_fields_target: "addButton"
|
246
249
|
}
|
247
250
|
) do
|
248
|
-
|
251
|
+
render_nested_fields_add_button_content(context.name)
|
249
252
|
end
|
250
253
|
end
|
251
254
|
end
|
252
255
|
|
253
|
-
def
|
256
|
+
def render_nested_fields_add_button_content(name)
|
254
257
|
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
258
|
render Phlex::TablerIcons::Plus.new(class: "w-4 h-4 mr-1")
|
256
259
|
span { "Add #{name.to_s.singularize.humanize}" }
|
@@ -94,7 +94,7 @@ module Plutonium
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def render_footer
|
97
|
-
div(class: "sticky bottom-[-2px] mt-1 p-4 pb-6 w-full z-50 bg-gray-50 dark:bg-gray-900") {
|
97
|
+
div(class: "sticky dyna:static 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
data/package-lock.json
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "@radioactive-labs/plutonium",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.6",
|
4
4
|
"lockfileVersion": 3,
|
5
5
|
"requires": true,
|
6
6
|
"packages": {
|
7
7
|
"": {
|
8
8
|
"name": "@radioactive-labs/plutonium",
|
9
|
-
"version": "0.1.
|
9
|
+
"version": "0.1.6",
|
10
10
|
"license": "MIT",
|
11
11
|
"dependencies": {
|
12
12
|
"@hotwired/stimulus": "^3.2.2",
|
data/package.json
CHANGED
data/tailwind.config.js
CHANGED
@@ -1,9 +1,19 @@
|
|
1
1
|
/** @type {import('tailwindcss').Config} */
|
2
2
|
|
3
|
+
const tailwindPlugin = require('tailwindcss/plugin')
|
3
4
|
const options = require("./tailwind.options.js")
|
4
5
|
|
5
6
|
export const content = options.content
|
6
7
|
export const darkMode = options.darkMode
|
7
|
-
export const plugins = options.plugins.map((plugin)
|
8
|
+
export const plugins = options.plugins.map(function (plugin) {
|
9
|
+
switch (typeof plugin) {
|
10
|
+
case "function":
|
11
|
+
return tailwindPlugin(plugin)
|
12
|
+
case "string":
|
13
|
+
return require(plugin)
|
14
|
+
default:
|
15
|
+
throw Error(`unsupported plugin: ${plugin}: ${(typeof plugin)}`)
|
16
|
+
}
|
17
|
+
})
|
8
18
|
export const theme = options.theme
|
9
19
|
export const safelist = options.safelist
|
data/tailwind.options.js
CHANGED
@@ -12,7 +12,13 @@ export const darkMode = "selector";
|
|
12
12
|
export const plugins = [
|
13
13
|
// requires users to have the required packages installed in their own project.
|
14
14
|
"@tailwindcss/forms",
|
15
|
-
"flowbite/plugin"
|
15
|
+
"flowbite/plugin",
|
16
|
+
function ({ addVariant }) {
|
17
|
+
// This creates a variant that applies when an ancestor has data-dyna="true"
|
18
|
+
// addVariant('dyna-frame', '&:has([data-dyna-frame="true"] &)')
|
19
|
+
// Or if you prefer using a class instead of data attribute:
|
20
|
+
addVariant('dyna', ':where(.dyna, .dyna *) &')
|
21
|
+
}
|
16
22
|
];
|
17
23
|
export const theme = {
|
18
24
|
extend: {
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plutonium
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.15.
|
4
|
+
version: 0.15.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Froelich
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -1039,6 +1039,7 @@ files:
|
|
1039
1039
|
- docs/guide/getting-started/index.md
|
1040
1040
|
- docs/guide/getting-started/installation.md
|
1041
1041
|
- docs/guide/getting-started/resources.md
|
1042
|
+
- docs/guide/tutorial.md
|
1042
1043
|
- docs/guide/what-is-plutonium.md
|
1043
1044
|
- docs/index.md
|
1044
1045
|
- docs/markdown-examples.md
|
@@ -1052,6 +1053,15 @@ files:
|
|
1052
1053
|
- docs/public/site.webmanifest
|
1053
1054
|
- docs/public/templates/base.rb
|
1054
1055
|
- docs/public/templates/plutonium.rb
|
1056
|
+
- docs/public/tutorial/plutonium-association-panel.png
|
1057
|
+
- docs/public/tutorial/plutonium-dashboard.png
|
1058
|
+
- docs/public/tutorial/plutonium-login-page.png
|
1059
|
+
- docs/public/tutorial/plutonium-nested-form.png
|
1060
|
+
- docs/public/tutorial/plutonium-posts-dashboard-customized.png
|
1061
|
+
- docs/public/tutorial/plutonium-posts-dashboard.png
|
1062
|
+
- docs/public/tutorial/plutonium-posts-detail-customized.png
|
1063
|
+
- docs/public/tutorial/plutonium-posts-detail.png
|
1064
|
+
- docs/public/tutorial/plutonium-publish-post.png
|
1055
1065
|
- esbuild.config.js
|
1056
1066
|
- exe/pug
|
1057
1067
|
- gemfiles/rails_7.gemfile
|
@@ -1083,6 +1093,8 @@ files:
|
|
1083
1093
|
- lib/generators/pu/docker/install/templates/docker-compose.yml
|
1084
1094
|
- lib/generators/pu/eject/layout/layout_generator.rb
|
1085
1095
|
- lib/generators/pu/eject/shell/shell_generator.rb
|
1096
|
+
- lib/generators/pu/extra/colorized_logger/colorized_logger_generator.rb
|
1097
|
+
- lib/generators/pu/extra/colorized_logger/templates/config/initializers/colorized_logger.rb
|
1086
1098
|
- lib/generators/pu/field/input/input_generator.rb
|
1087
1099
|
- lib/generators/pu/field/input/templates/.keep
|
1088
1100
|
- lib/generators/pu/field/input/templates/input.rb.tt
|