phlex-reactive 0.13.2 → 0.13.3
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/CHANGELOG.md +135 -0
- data/README.md +77 -9
- data/app/controllers/phlex/reactive/actions_controller.rb +72 -3
- data/app/javascript/phlex/reactive/compute.js +9 -1
- data/app/javascript/phlex/reactive/compute.min.js.map +2 -2
- data/app/javascript/phlex/reactive/confirm_predicate.js +7 -1
- data/app/javascript/phlex/reactive/confirm_predicate.min.js.map +2 -2
- data/app/javascript/phlex/reactive/reactive_controller.js +417 -51
- data/app/javascript/phlex/reactive/reactive_controller.min.js +2 -2
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +3 -3
- data/lib/phlex/reactive/component/dsl.rb +7 -0
- data/lib/phlex/reactive/component/helpers.rb +4 -3
- data/lib/phlex/reactive/component.rb +2 -1
- data/lib/phlex/reactive/param_schema.rb +16 -0
- data/lib/phlex/reactive/test_helpers.rb +10 -3
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +1 -1
|
@@ -389,6 +389,13 @@ module Phlex
|
|
|
389
389
|
# inputs: { title: :string, qty: :number },
|
|
390
390
|
# outputs: %i[title_preview char_count]
|
|
391
391
|
#
|
|
392
|
+
# A CHECKBOX contributes its checked state, never its constant .value
|
|
393
|
+
# (issue #262): 1/0 as a :number, "true"/"false" as a :string, and a
|
|
394
|
+
# real boolean as a :boolean. It wins over the hidden companion Rails
|
|
395
|
+
# renders before it. A radio group contributes its checked radio's value.
|
|
396
|
+
#
|
|
397
|
+
# reactive_compute :total, inputs: [:price, { gift_wrap: :boolean }], outputs: %i[total]
|
|
398
|
+
#
|
|
392
399
|
# An output with no matching form field writes to its reactive_text(:name)
|
|
393
400
|
# node (textContent); a declared input also mirrors into its own text node
|
|
394
401
|
# with no reducer. The array form's wire stays byte-identical.
|
|
@@ -449,7 +449,7 @@ module Phlex
|
|
|
449
449
|
# the baseline is the DOM's own `defaultValue`/`defaultChecked`/
|
|
450
450
|
# `defaultSelected` from the last server render (dirty = current ≠ default).
|
|
451
451
|
# The descriptor deep-merges via mix, so a caller's own data-action is
|
|
452
|
-
# token-joined, not clobbered (
|
|
452
|
+
# token-joined, not clobbered (AGENTS.md Never-Do #8).
|
|
453
453
|
def reactive_field(param, **attrs)
|
|
454
454
|
# Issue #184: the removed dirty: kwarg lands in **attrs — catch it and
|
|
455
455
|
# print the reactive_dirty rewrite.
|
|
@@ -1232,8 +1232,9 @@ module Phlex
|
|
|
1232
1232
|
# The inputs param wire (issue #104). Untyped (array form) → a JSON ARRAY of
|
|
1233
1233
|
# names, byte-identical to the shipped wire so the client keeps its numeric
|
|
1234
1234
|
# coercion. Typed (hash form) → a JSON OBJECT of name→type
|
|
1235
|
-
# ({"title":"string","qty":"number"}) so the client reads a :string raw
|
|
1236
|
-
# coerces a :number through Number
|
|
1235
|
+
# ({"title":"string","qty":"number"}) so the client reads a :string raw,
|
|
1236
|
+
# coerces a :number through Number and reads a :boolean as a checkbox's
|
|
1237
|
+
# checked state (issue #262).
|
|
1237
1238
|
def compute_inputs_param(definition)
|
|
1238
1239
|
types = definition.input_types
|
|
1239
1240
|
return definition.inputs.map(&:to_s).to_json if types.nil?
|
|
@@ -102,7 +102,8 @@ module Phlex
|
|
|
102
102
|
# `input_types` (issue #104) is nil for the ARRAY input form (untyped ⇒ the
|
|
103
103
|
# client coerces every input through Number, the shipped behavior) and a
|
|
104
104
|
# { name => type } hash for the typed HASH form (:string reads the field
|
|
105
|
-
# value raw, :number coerces
|
|
105
|
+
# value raw, :number coerces, :boolean reads a checkbox's checked state —
|
|
106
|
+
# issue #262). `inputs` stays the ordered name list either
|
|
106
107
|
# way, so iteration order is preserved and the array-form wire is unchanged.
|
|
107
108
|
#
|
|
108
109
|
# `mirror` (issue #159) is nil when undeclared, else a { name => [id
|
|
@@ -174,6 +174,22 @@ module Phlex
|
|
|
174
174
|
coerced.equal?(DROP) ? {} : coerced
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
+
# The key this DECLARATION holds for `name` if it declares it as an ARRAY
|
|
178
|
+
# param, else nil. Reads the declaration, never the incoming params, so the
|
|
179
|
+
# endpoint's empty-group announcement (issue #258) can only ever fill
|
|
180
|
+
# something the action asked for.
|
|
181
|
+
#
|
|
182
|
+
# A key that is neither a String nor a Symbol stays unresolved on purpose:
|
|
183
|
+
# `compile` accepts `{ 0 => [:string] }`, but `coerce_hash` raises on
|
|
184
|
+
# `0.to_sym` the moment that key is PRESENT, and the announcement is what
|
|
185
|
+
# would make it present. Resolving it would turn a request that answers 200
|
|
186
|
+
# and fills nothing into a 500.
|
|
187
|
+
def array_param(name)
|
|
188
|
+
name = name.to_s
|
|
189
|
+
key, type = @schema.find { |k, _| (k.is_a?(String) || k.is_a?(Symbol)) && k.to_s == name }
|
|
190
|
+
key if type.is_a?(Array)
|
|
191
|
+
end
|
|
192
|
+
|
|
177
193
|
private
|
|
178
194
|
|
|
179
195
|
# Coerce a value against a declared type. Arrays accept both a real JSON
|
|
@@ -64,10 +64,17 @@ module Phlex
|
|
|
64
64
|
# `:file` param is present, issue #34): token + act flat, params bracketed.
|
|
65
65
|
# No JSON Content-Type — Rails' test `post` builds the multipart body from
|
|
66
66
|
# the nested Hash (files ride as UploadedFile values).
|
|
67
|
-
|
|
67
|
+
#
|
|
68
|
+
# `empty_groups:` names the `[]` groups the client cleared (issue #258). A
|
|
69
|
+
# form body cannot carry an empty array, so the client leaves those keys
|
|
70
|
+
# out and announces them in a field of its own; passing the names here is
|
|
71
|
+
# how a spec reproduces that request. Omit it and the body is exactly what
|
|
72
|
+
# it was before the field existed.
|
|
73
|
+
def post_reactive_multipart(component_or_class, act, params: {}, payload: {}, empty_groups: [])
|
|
68
74
|
token = reactive_action_token(component_or_class, payload)
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
body = { token:, act:, params: }
|
|
76
|
+
body[:empty_groups] = empty_groups if empty_groups.present?
|
|
77
|
+
post Phlex::Reactive.action_path, params: body,
|
|
71
78
|
headers: { "Accept" => "text/vnd.turbo-stream.html" }
|
|
72
79
|
end
|
|
73
80
|
|