phlex-reactive 0.16.0 → 0.16.2
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 +282 -145
- data/README.md +85 -1
- data/app/controllers/phlex/reactive/actions_controller.rb +10 -0
- data/app/javascript/phlex/reactive/core.js +1 -0
- data/app/javascript/phlex/reactive/core.min.js +4 -4
- data/app/javascript/phlex/reactive/core.min.js.map +4 -4
- data/app/javascript/phlex/reactive/features/bindings.js +81 -4
- data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
- data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
- data/app/javascript/phlex/reactive/features/drag.js +522 -0
- data/app/javascript/phlex/reactive/features/drag.min.js +4 -0
- data/app/javascript/phlex/reactive/features/drag.min.js.map +10 -0
- data/app/javascript/phlex/reactive/reactive_controller.js +2 -0
- data/app/javascript/phlex/reactive/reactive_controller.min.js +4 -4
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +7 -6
- data/app/javascript/phlex/reactive/runtime.js +57 -23
- data/lib/phlex/reactive/component/css_vars_binding.rb +118 -0
- data/lib/phlex/reactive/component/drag.rb +270 -0
- data/lib/phlex/reactive/component/dsl.rb +3 -0
- data/lib/phlex/reactive/component/helpers.rb +5 -1
- data/lib/phlex/reactive/component/registry.rb +15 -5
- data/lib/phlex/reactive/engine.rb +14 -5
- data/lib/phlex/reactive/held_fields.rb +21 -1
- data/lib/phlex/reactive/param_schema.rb +5 -1
- data/lib/phlex/reactive/response.rb +55 -4
- data/lib/phlex/reactive/shareable.rb +18 -3
- data/lib/phlex/reactive/test_helpers.rb +6 -0
- data/lib/phlex/reactive/version.rb +1 -1
- data/sig/phlex/reactive/response.rbs +3 -0
- metadata +6 -1
|
@@ -103,6 +103,13 @@ module Phlex
|
|
|
103
103
|
# attribute fragments), shared by warm! so a non-main Ractor can read it.
|
|
104
104
|
MEMO_IVARS = %i[@reactive_record_ivar @reactive_state_ivars @reactive_effect_attrs @reactive_on_complete_attr].freeze
|
|
105
105
|
|
|
106
|
+
# Every class ivar warm! leaves for a request to read, each shared
|
|
107
|
+
# deeply so a non-main Ractor can read it: the class's own
|
|
108
|
+
# declarations, the resolution cache and the render path's memos.
|
|
109
|
+
# (The generation counters next to them are Integers, shareable as
|
|
110
|
+
# they are.)
|
|
111
|
+
WARMED_IVARS = [*OWN_IVARS.values, :@reactive_registry_cache, *MEMO_IVARS].freeze
|
|
112
|
+
|
|
106
113
|
# Every class with the reactive DSL, a ClientBindings-only view
|
|
107
114
|
# included (Streamable's registry has only the token-bearing ones):
|
|
108
115
|
# what the engine warms at boot. Per-Ractor (Phlex::Reactive::Store);
|
|
@@ -161,7 +168,7 @@ module Phlex
|
|
|
161
168
|
|
|
162
169
|
# Resolve every registry kind and build the render path's per-class
|
|
163
170
|
# memos for each reactive component class among `classes`, then
|
|
164
|
-
# freeze each class
|
|
171
|
+
# share (deep-freeze) what each class keeps (WARMED_IVARS). The engine runs it at boot
|
|
165
172
|
# when the app eager-loads (every class is loaded by then), so a
|
|
166
173
|
# request reads memos and never writes the class. A declaration
|
|
167
174
|
# after it (a development reload) bumps the generation and the next
|
|
@@ -226,10 +233,14 @@ module Phlex
|
|
|
226
233
|
end
|
|
227
234
|
|
|
228
235
|
# The class's own storage for `kind`, initialized from the block on
|
|
229
|
-
# first write.
|
|
236
|
+
# first write. warm! shares (freezes) it, so a declaration after
|
|
237
|
+
# warm! (a development reload) writes to a fresh copy.
|
|
230
238
|
def own!(klass, kind)
|
|
231
239
|
ivar = OWN_IVARS.fetch(kind)
|
|
232
|
-
|
|
240
|
+
current = klass.instance_variable_get(ivar)
|
|
241
|
+
return current if current && !current.frozen?
|
|
242
|
+
|
|
243
|
+
klass.instance_variable_set(ivar, current ? current.dup : yield)
|
|
233
244
|
end
|
|
234
245
|
|
|
235
246
|
# Invalidate every resolution memo in the process (generation bump)
|
|
@@ -257,8 +268,7 @@ module Phlex
|
|
|
257
268
|
|
|
258
269
|
def warm_class(klass)
|
|
259
270
|
[*READERS.values, *MEMO_READERS].each { klass.public_send(it) if klass.respond_to?(it) }
|
|
260
|
-
|
|
261
|
-
MEMO_IVARS.each do
|
|
271
|
+
WARMED_IVARS.each do
|
|
262
272
|
next unless klass.instance_variable_defined?(it)
|
|
263
273
|
|
|
264
274
|
klass.instance_variable_set(it, Phlex::Reactive::Shareable.share(klass.instance_variable_get(it)))
|
|
@@ -20,10 +20,12 @@ module Phlex
|
|
|
20
20
|
# build script (scripts/build_client.js), the core's own table and the
|
|
21
21
|
# default entry name the same features —
|
|
22
22
|
# spec/phlex/engine_client_pin_spec.rb fails when they disagree.
|
|
23
|
-
CLIENT_FEATURES = %w[
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#
|
|
23
|
+
CLIENT_FEATURES = %w[
|
|
24
|
+
persist defer form bindings compute sortable drag presence countdown effects hints devtools
|
|
25
|
+
].freeze
|
|
26
|
+
# (Gzipped, bun zlib level 9, KB = 1,024 B: the default file 39.5 KB; the core 17.9 KB;
|
|
27
|
+
# persist 3.2, defer 2.9, form 1.2, bindings 8.0, compute 2.0, sortable
|
|
28
|
+
# 3.1, drag 3.5, presence 1.3, countdown 0.9, effects 1.7, hints 0.9, devtools 1.5 KB. spec/javascript/bundle_budget.test.js
|
|
27
29
|
# is the source of truth and holds a ratchet on each.)
|
|
28
30
|
|
|
29
31
|
# Mount POST /reactive/actions -> Phlex::Reactive::ActionsController#create
|
|
@@ -32,13 +34,20 @@ module Phlex
|
|
|
32
34
|
# (the privately cacheable lazy render, issue #277). Apps can change the
|
|
33
35
|
# paths with Phlex::Reactive.action_path / .defer_path / .fragment_path
|
|
34
36
|
# before boot.
|
|
37
|
+
#
|
|
38
|
+
# Rails main's RouteSet#finalize! makes the route set Ractor-shareable,
|
|
39
|
+
# append blocks included, and a block's self here is the engine, which
|
|
40
|
+
# no Ractor can share. Ractor.shareable_proc (Ruby 4.0+) gives the block
|
|
41
|
+
# a nil self; Mapper evaluates it with its own self either way.
|
|
35
42
|
initializer "phlex_reactive.routes" do
|
|
36
|
-
|
|
43
|
+
routes = proc do
|
|
37
44
|
post Phlex::Reactive.action_path, to: "phlex/reactive/actions#create", as: :phlex_reactive_action
|
|
38
45
|
post Phlex::Reactive.defer_path, to: "phlex/reactive/actions#deferred", as: :phlex_reactive_defer
|
|
39
46
|
get "#{Phlex::Reactive.fragment_path}/:id", to: "phlex/reactive/fragments#show",
|
|
40
47
|
as: :phlex_reactive_fragment
|
|
41
48
|
end
|
|
49
|
+
routes = ::Ractor.shareable_proc(&routes) if defined?(::Ractor) && ::Ractor.respond_to?(:shareable_proc)
|
|
50
|
+
it.routes.append(&routes)
|
|
42
51
|
end
|
|
43
52
|
|
|
44
53
|
# Make the MINIFIED client build available to Propshaft/Sprockets so it can
|
|
@@ -14,9 +14,15 @@ module Phlex
|
|
|
14
14
|
#
|
|
15
15
|
# def save(title: nil)
|
|
16
16
|
# authorize!(@note, to: :update?)
|
|
17
|
-
# return
|
|
17
|
+
# return reply.morph.refused("title") if reactive_held_fields.include?("title") && title != @note.title
|
|
18
18
|
# ...
|
|
19
19
|
#
|
|
20
|
+
# A reply to the root's own request releases its marks: it answers exactly
|
|
21
|
+
# those values. A refusal (`reply.morph.refused`, issue #413) is sent back
|
|
22
|
+
# in the X-Reactive-Refused header, and the client keeps the marks it names,
|
|
23
|
+
# so the user's next keystroke is refused too until the action stores the
|
|
24
|
+
# field or the user takes the other value.
|
|
25
|
+
#
|
|
20
26
|
# The header is UNTRUSTED input, like every request field: it carries names
|
|
21
27
|
# only, never values, and is read as data — capped in bytes, count and
|
|
22
28
|
# length, non-strings dropped, and filtered to the names the running action
|
|
@@ -26,6 +32,8 @@ module Phlex
|
|
|
26
32
|
# It is a hint about what the user saw, not authorization.
|
|
27
33
|
module HeldFields
|
|
28
34
|
HEADER = "X-Reactive-Held"
|
|
35
|
+
# The response header a refusing reply sends (issue #413).
|
|
36
|
+
REFUSED_HEADER = "X-Reactive-Refused"
|
|
29
37
|
|
|
30
38
|
# A raw header longer than this is ignored unparsed.
|
|
31
39
|
MAX_BYTES = 4_096
|
|
@@ -62,6 +70,18 @@ module Phlex
|
|
|
62
70
|
EMPTY
|
|
63
71
|
end
|
|
64
72
|
|
|
73
|
+
# The X-Reactive-Refused value for Response#refused_fields: "*" keeps
|
|
74
|
+
# every mark, a JSON list keeps those names. ASCII only (a header is a
|
|
75
|
+
# byte string), as the client escapes X-Reactive-Held. A list past
|
|
76
|
+
# MAX_BYTES (a header a proxy may refuse) widens to "*": keeping more
|
|
77
|
+
# marks than named is safe, losing the refusal is not.
|
|
78
|
+
def refused_header(fields)
|
|
79
|
+
return "*" if fields == :all
|
|
80
|
+
|
|
81
|
+
json = JSON.generate(fields, ascii_only: true)
|
|
82
|
+
json.bytesize > MAX_BYTES ? "*" : json
|
|
83
|
+
end
|
|
84
|
+
|
|
65
85
|
# Keep the strings among `names` that `action_def` declares as params.
|
|
66
86
|
def filter(names, action_def)
|
|
67
87
|
declared = declared_names(action_def)
|
|
@@ -143,8 +143,12 @@ module Phlex
|
|
|
143
143
|
|
|
144
144
|
attr_reader :schema
|
|
145
145
|
|
|
146
|
+
# Ractor-shareable once built: a deep-frozen copy of the declared schema
|
|
147
|
+
# (the app's own Hash is left alone) and nothing written after this, so
|
|
148
|
+
# a warmed action definition can be read from a non-main Ractor.
|
|
146
149
|
def initialize(schema)
|
|
147
|
-
@schema = schema
|
|
150
|
+
@schema = Phlex::Reactive::Shareable.share(schema)
|
|
151
|
+
freeze
|
|
148
152
|
end
|
|
149
153
|
|
|
150
154
|
# Coerce client params against this schema. Anything not in the schema is
|
|
@@ -19,6 +19,7 @@ module Phlex
|
|
|
19
19
|
# reply.remove # drop the element (e.g. moderation queue)
|
|
20
20
|
# reply.redirect(article_url(@article)) # slug changed -> Turbo.visit the new URL
|
|
21
21
|
# reply.replace.stream(Totals.update(@order)) # multi-stream
|
|
22
|
+
# reply.morph.refused("title") # answered, not stored: keep the field held
|
|
22
23
|
class Response
|
|
23
24
|
attr_reader :streams, :redirect_url, :token_component, :subject_component
|
|
24
25
|
|
|
@@ -351,8 +352,11 @@ data-reactive-ops="#{ERB::Util.html_escape(json)}"></turbo-stream>).html_safe
|
|
|
351
352
|
# reply never allocates an empty array per Response.
|
|
352
353
|
NO_SEGMENTS = [].freeze
|
|
353
354
|
|
|
355
|
+
# refused: what #refused recorded (issue #413) — nil, :all, or a frozen
|
|
356
|
+
# list of field names.
|
|
354
357
|
def initialize(streams: [], redirect_url: nil, render_self: true, token_component: nil,
|
|
355
|
-
subject_component: nil, deferred_segments: NO_SEGMENTS, pending_segments: NO_SEGMENTS
|
|
358
|
+
subject_component: nil, deferred_segments: NO_SEGMENTS, pending_segments: NO_SEGMENTS,
|
|
359
|
+
refused: nil)
|
|
356
360
|
@streams = streams.freeze
|
|
357
361
|
@redirect_url = redirect_url
|
|
358
362
|
@render_self = render_self
|
|
@@ -360,9 +364,14 @@ data-reactive-ops="#{ERB::Util.html_escape(json)}"></turbo-stream>).html_safe
|
|
|
360
364
|
@subject_component = subject_component
|
|
361
365
|
@deferred_segments = deferred_segments.freeze
|
|
362
366
|
@pending_segments = pending_segments.freeze
|
|
367
|
+
@refused_fields = refused
|
|
363
368
|
freeze
|
|
364
369
|
end
|
|
365
370
|
|
|
371
|
+
# What the reply refused (issue #413): :all, the field names, or nil when
|
|
372
|
+
# it refused nothing.
|
|
373
|
+
attr_reader :refused_fields
|
|
374
|
+
|
|
366
375
|
# The recorded reply.defer segments (issue #165), in call order. The
|
|
367
376
|
# Response only RECORDS them; the endpoint (via the Defer module) builds
|
|
368
377
|
# the placeholder + directive streams AFTER the action's transaction
|
|
@@ -388,7 +397,8 @@ data-reactive-ops="#{ERB::Util.html_escape(json)}"></turbo-stream>).html_safe
|
|
|
388
397
|
token_component: @token_component,
|
|
389
398
|
subject_component: @subject_component,
|
|
390
399
|
deferred_segments: @deferred_segments,
|
|
391
|
-
pending_segments: @pending_segments
|
|
400
|
+
pending_segments: @pending_segments,
|
|
401
|
+
refused: @refused_fields
|
|
392
402
|
)
|
|
393
403
|
end
|
|
394
404
|
|
|
@@ -420,7 +430,8 @@ data-reactive-ops="#{ERB::Util.html_escape(json)}"></turbo-stream>).html_safe
|
|
|
420
430
|
subject_component: @subject_component,
|
|
421
431
|
deferred_segments: @deferred_segments +
|
|
422
432
|
[Phlex::Reactive::Defer::Segment.new(component:, placeholder:, morph:)],
|
|
423
|
-
pending_segments: @pending_segments
|
|
433
|
+
pending_segments: @pending_segments,
|
|
434
|
+
refused: @refused_fields
|
|
424
435
|
)
|
|
425
436
|
end
|
|
426
437
|
|
|
@@ -464,10 +475,50 @@ data-reactive-ops="#{ERB::Util.html_escape(json)}"></turbo-stream>).html_safe
|
|
|
464
475
|
token_component: @token_component || built.token_component,
|
|
465
476
|
subject_component: @subject_component,
|
|
466
477
|
deferred_segments: @deferred_segments,
|
|
467
|
-
pending_segments: @pending_segments + built.pending_segments
|
|
478
|
+
pending_segments: @pending_segments + built.pending_segments,
|
|
479
|
+
refused: @refused_fields
|
|
480
|
+
)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Answer the posted values without storing them (issue #413). Every reply
|
|
484
|
+
# to a root's own request releases the fields a morph held (issue #370),
|
|
485
|
+
# because it answers exactly those values. A reply that refused them says
|
|
486
|
+
# so, and the client keeps the marks: the root's next action names the
|
|
487
|
+
# same fields in X-Reactive-Held again, instead of saving over the other
|
|
488
|
+
# change on the user's next keystroke.
|
|
489
|
+
#
|
|
490
|
+
# if reactive_held_fields.include?("title") && title != @note.title
|
|
491
|
+
# @conflict = title
|
|
492
|
+
# return reply.morph.refused("title")
|
|
493
|
+
# end
|
|
494
|
+
#
|
|
495
|
+
# No names keeps every mark the root holds; names (the fields' `name`
|
|
496
|
+
# attributes, as reactive_held_fields reports them) keep only those, and
|
|
497
|
+
# the root's other marks release as usual. Nothing else about the reply
|
|
498
|
+
# changes: it is a 200, with no error marker and no reactive:error.
|
|
499
|
+
# Returns a NEW Response (immutable). Dead on a redirect, like #defer.
|
|
500
|
+
def refused(*names)
|
|
501
|
+
if redirect?
|
|
502
|
+
raise Phlex::Reactive::Error,
|
|
503
|
+
"reply.refused on a redirect reply is dead — the client is navigating away, " \
|
|
504
|
+
"so there is no held field left to keep"
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
names = names.flatten.map { -it.to_s }.uniq.freeze
|
|
508
|
+
self.class.new(
|
|
509
|
+
streams: @streams,
|
|
510
|
+
redirect_url: @redirect_url,
|
|
511
|
+
render_self: @render_self,
|
|
512
|
+
token_component: @token_component,
|
|
513
|
+
subject_component: @subject_component,
|
|
514
|
+
deferred_segments: @deferred_segments,
|
|
515
|
+
pending_segments: @pending_segments,
|
|
516
|
+
refused: names.empty? ? :all : names
|
|
468
517
|
)
|
|
469
518
|
end
|
|
470
519
|
|
|
520
|
+
def refused? = !@refused_fields.nil?
|
|
521
|
+
|
|
471
522
|
# Chain a `reactive:js` op stream — server-pushed client DOM ops (issue
|
|
472
523
|
# #97) — onto this reply, so a focus/dispatch/class toggle runs on the
|
|
473
524
|
# client after the render applies:
|
|
@@ -23,6 +23,10 @@ module Phlex
|
|
|
23
23
|
# works on the main Ractor only;
|
|
24
24
|
# * a Hash keeps its default value or default proc and its
|
|
25
25
|
# compare_by_identity;
|
|
26
|
+
# * a Data value (the gem's declarations: an action, a compute, a
|
|
27
|
+
# collection) comes back as a copy with every member shared, when
|
|
28
|
+
# its class keeps Data's own initialize (one of its own may
|
|
29
|
+
# normalize or validate, and is never run again);
|
|
26
30
|
# * any other object (a MessageVerifier, an APM adapter, an app object)
|
|
27
31
|
# is kept as is.
|
|
28
32
|
#
|
|
@@ -35,6 +39,7 @@ module Phlex
|
|
|
35
39
|
when ::Proc then share_proc(value.dup) || value
|
|
36
40
|
when ::Array then seal(value.map { share(it) })
|
|
37
41
|
when ::Hash then share_hash(value)
|
|
42
|
+
when ::Data then plain_data?(value) ? share_data(value) : value
|
|
38
43
|
when ::String then value.dup.freeze
|
|
39
44
|
else value
|
|
40
45
|
end
|
|
@@ -60,12 +65,14 @@ module Phlex
|
|
|
60
65
|
end
|
|
61
66
|
|
|
62
67
|
# The Proc made shareable in place, or nil when it cannot be (or there
|
|
63
|
-
# is no Ractor).
|
|
68
|
+
# is no Ractor). Ruby raises Ractor::IsolationError for an unshareable
|
|
69
|
+
# self or capture, and ArgumentError for a Proc that assigns a variable
|
|
70
|
+
# it captures.
|
|
64
71
|
def share_proc(callable)
|
|
65
72
|
return unless defined?(::Ractor)
|
|
66
73
|
|
|
67
74
|
::Ractor.make_shareable(callable)
|
|
68
|
-
rescue ::Ractor::Error
|
|
75
|
+
rescue ::Ractor::Error, ::ArgumentError
|
|
69
76
|
nil
|
|
70
77
|
end
|
|
71
78
|
|
|
@@ -78,10 +85,18 @@ module Phlex
|
|
|
78
85
|
seal(copy)
|
|
79
86
|
end
|
|
80
87
|
|
|
88
|
+
# Data#with runs the class's initialize again: only Data's own, which
|
|
89
|
+
# just assigns the members, is safe to.
|
|
90
|
+
def plain_data?(data) = data.class.instance_method(:initialize).owner.equal?(::Data)
|
|
91
|
+
|
|
92
|
+
# A copy with every member shared. Data is always frozen, so the copy
|
|
93
|
+
# is shareable when every member is.
|
|
94
|
+
def share_data(data) = data.with(**data.to_h.transform_values { share(it) })
|
|
95
|
+
|
|
81
96
|
# Frozen; shareable as well when everything inside it is.
|
|
82
97
|
def seal(collection) = collection.freeze
|
|
83
98
|
|
|
84
|
-
private_class_method :share_proc, :share_hash, :seal
|
|
99
|
+
private_class_method :share_proc, :share_hash, :plain_data?, :share_data, :seal
|
|
85
100
|
end
|
|
86
101
|
end
|
|
87
102
|
end
|
|
@@ -271,6 +271,12 @@ module Phlex
|
|
|
271
271
|
@response&.redirect_url
|
|
272
272
|
end
|
|
273
273
|
|
|
274
|
+
# The action answered without storing (reply.morph.refused, issue #413):
|
|
275
|
+
# the client keeps the fields it held.
|
|
276
|
+
def refused?
|
|
277
|
+
!@response.nil? && @response.refused?
|
|
278
|
+
end
|
|
279
|
+
|
|
274
280
|
# The actor's turbo-stream strings, exactly as the endpoint would render
|
|
275
281
|
# them (response_streams) — a legacy return is the single self-replace; a
|
|
276
282
|
# Response is honored, with the same token-refresh streams appended.
|
|
@@ -9,6 +9,7 @@ module Phlex
|
|
|
9
9
|
attr_reader subject_component: untyped
|
|
10
10
|
attr_reader deferred_segments: Array[untyped]
|
|
11
11
|
attr_reader pending_segments: Array[untyped]
|
|
12
|
+
attr_reader refused_fields: (:all | Array[String])?
|
|
12
13
|
|
|
13
14
|
# --- chaining ---
|
|
14
15
|
def stream: (*String more) -> Response
|
|
@@ -17,6 +18,7 @@ module Phlex
|
|
|
17
18
|
def also: (?untyped companion, ?morph: bool, **untyped targets) -> Response
|
|
18
19
|
def defer: (untyped component, ?placeholder: untyped, ?morph: bool) -> Response
|
|
19
20
|
def pending: (untyped records, ?peers: bool, ?job: untyped, ?args: untyped, **untyped opts) ?{ (?) -> untyped } -> Response
|
|
21
|
+
def refused: (*(String | Symbol | Array[String | Symbol]) names) -> Response
|
|
20
22
|
|
|
21
23
|
# Removed: each raises a guided error naming its replacement.
|
|
22
24
|
def also_update: (untyped target, **untyped) -> bot
|
|
@@ -28,6 +30,7 @@ module Phlex
|
|
|
28
30
|
def refresh_token?: () -> bool
|
|
29
31
|
def deferred?: () -> bool
|
|
30
32
|
def pending?: () -> bool
|
|
33
|
+
def refused?: () -> bool
|
|
31
34
|
|
|
32
35
|
# --- the builders `reply` delegates to ---
|
|
33
36
|
def self.build_replace: (untyped component, ?morph: bool, ?effect: untyped) -> Response
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phlex-reactive
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.16.
|
|
4
|
+
version: 0.16.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -149,6 +149,9 @@ files:
|
|
|
149
149
|
- app/javascript/phlex/reactive/features/devtools.js
|
|
150
150
|
- app/javascript/phlex/reactive/features/devtools.min.js
|
|
151
151
|
- app/javascript/phlex/reactive/features/devtools.min.js.map
|
|
152
|
+
- app/javascript/phlex/reactive/features/drag.js
|
|
153
|
+
- app/javascript/phlex/reactive/features/drag.min.js
|
|
154
|
+
- app/javascript/phlex/reactive/features/drag.min.js.map
|
|
152
155
|
- app/javascript/phlex/reactive/features/effects.js
|
|
153
156
|
- app/javascript/phlex/reactive/features/effects.min.js
|
|
154
157
|
- app/javascript/phlex/reactive/features/effects.min.js.map
|
|
@@ -197,6 +200,8 @@ files:
|
|
|
197
200
|
- lib/phlex/reactive/collections.rb
|
|
198
201
|
- lib/phlex/reactive/component.rb
|
|
199
202
|
- lib/phlex/reactive/component/countdown_binding.rb
|
|
203
|
+
- lib/phlex/reactive/component/css_vars_binding.rb
|
|
204
|
+
- lib/phlex/reactive/component/drag.rb
|
|
200
205
|
- lib/phlex/reactive/component/dsl.rb
|
|
201
206
|
- lib/phlex/reactive/component/editable.rb
|
|
202
207
|
- lib/phlex/reactive/component/field_references.rb
|