phoenix_live_view 0.17.2 → 0.17.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.
- package/CHANGELOG.md +18 -1
- package/README.md +14 -14
- package/assets/js/phoenix_live_view/constants.js +1 -0
- package/assets/js/phoenix_live_view/js.js +41 -29
- package/assets/js/phoenix_live_view/live_socket.js +5 -4
- package/assets/js/phoenix_live_view/rendered.js +21 -9
- package/assets/js/phoenix_live_view/utils.js +4 -1
- package/assets/js/phoenix_live_view/view.js +10 -7
- package/package.json +1 -1
- package/priv/static/phoenix_live_view.cjs.js +80 -49
- package/priv/static/phoenix_live_view.cjs.js.map +2 -2
- package/priv/static/phoenix_live_view.esm.js +80 -49
- package/priv/static/phoenix_live_view.esm.js.map +2 -2
- package/priv/static/phoenix_live_view.js +80 -49
- package/priv/static/phoenix_live_view.min.js +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.3 (2021-10-28)
|
|
4
|
+
|
|
5
|
+
### Enhancements
|
|
6
|
+
- Support 3-tuple for JS class transitions to support staged animations where a transition class is applied with a starting and ending class
|
|
7
|
+
- Allow JS commands to be executed on DOM nodes outside of the LiveView container
|
|
8
|
+
|
|
9
|
+
### Optimization
|
|
10
|
+
- Avoid duplicate statics inside comprehension. In previous versions, comprehensions were able to avoid duplication only the content in their root. Now we recursively traverse all comprehension nodes and send the static only once for the whole comprehension. This should massively reduce the cost of sending comprehensions over the wire
|
|
11
|
+
|
|
12
|
+
### Bug fixes
|
|
13
|
+
- Fix HTML engine bug causing expressions to be duplicated or not rendered correctly
|
|
14
|
+
- Fix HTML engine bug causing slots to not be re-rendered when they should have
|
|
15
|
+
- Fix form recovery being sent to wrong target
|
|
16
|
+
|
|
3
17
|
## 0.17.2 (2021-10-22)
|
|
4
18
|
|
|
5
19
|
### Bug fixes
|
|
@@ -63,7 +77,7 @@ atom `:default`.
|
|
|
63
77
|
#### LEEx templates in stateful LiveComponents
|
|
64
78
|
|
|
65
79
|
Stateful LiveComponents (where an `:id` is given) must now return HEEx templates
|
|
66
|
-
(`~H` sigil or `.heex` extension). LEEx
|
|
80
|
+
(`~H` sigil or `.heex` extension). LEEx templates (`~L` sigil or `.leex` extension)
|
|
67
81
|
are no longer supported. This addresses bugs and allows stateful components
|
|
68
82
|
to be rendered more efficiently client-side.
|
|
69
83
|
|
|
@@ -302,6 +316,9 @@ Additionally on the client, the root LiveView element no longer exposes the
|
|
|
302
316
|
LiveView module name, therefore the `phx-view` attribute is never set.
|
|
303
317
|
Similarly, the `viewName` property of client hooks has been removed.
|
|
304
318
|
|
|
319
|
+
Codebases calling a custom function `component/3` should rename it or specify its module to avoid a conflict,
|
|
320
|
+
as LiveView introduces a macro with that name and it is special cased by the underlying engine.
|
|
321
|
+
|
|
305
322
|
### Enhancements
|
|
306
323
|
- Introduce HEEx templates
|
|
307
324
|
- Introduce `Phoenix.Component`
|
package/README.md
CHANGED
|
@@ -17,9 +17,14 @@ steps:
|
|
|
17
17
|
* Use a declarative model to render HTML on the server
|
|
18
18
|
over WebSockets with optional LongPolling fallback
|
|
19
19
|
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
* A rich templating language, called HEEx, with support
|
|
21
|
+
for function components, slots, HTML validation, and more
|
|
22
|
+
|
|
23
|
+
* Smart change tracking - after connected, LiveView sends
|
|
24
|
+
only what changed to the client, skipping the template
|
|
25
|
+
markup and reducing the payload. This makes LiveView
|
|
26
|
+
payloads much smaller than server-rendered HTML and on
|
|
27
|
+
par with fine-tuned SPA applications
|
|
23
28
|
|
|
24
29
|
* Live form validation with file upload support
|
|
25
30
|
|
|
@@ -27,9 +32,12 @@ steps:
|
|
|
27
32
|
`phx-focus`, `phx-blur`, `phx-submit`, etc. `phx-hook` is
|
|
28
33
|
included for the cases where you have to write JavaScript
|
|
29
34
|
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
* Perform optimistic updates and transitions via JavaScript
|
|
36
|
+
commands (`Phoenix.LiveView.JS`)
|
|
37
|
+
|
|
38
|
+
* Code reuse via stateful components, which break templates,
|
|
39
|
+
state, and event handling into reusable bits, which is essential
|
|
40
|
+
in large applications
|
|
33
41
|
|
|
34
42
|
* Live navigation to enrich links and redirects to only load the
|
|
35
43
|
minimum amount of content as users navigate between pages
|
|
@@ -106,14 +114,6 @@ anywhere else:
|
|
|
106
114
|
sent over the wire. This is achievable thanks to Elixir's
|
|
107
115
|
immutability and its ability to treat code as data.
|
|
108
116
|
|
|
109
|
-
* LiveView separates the static and dynamic parts of your templates.
|
|
110
|
-
When you first render a page, Phoenix LiveView renders and sends
|
|
111
|
-
the whole template to the browser. Then, for any new update, only
|
|
112
|
-
the modified dynamic content is resent. This alongside diff tracking
|
|
113
|
-
makes it so LiveView only sends a few bytes on every update, instead
|
|
114
|
-
of sending kilobytes on every other user interaction - which would
|
|
115
|
-
be detrimental to the user experience.
|
|
116
|
-
|
|
117
117
|
## Browser Support
|
|
118
118
|
|
|
119
119
|
All current Chrome, Safari, Firefox, and MS Edge are supported.
|
|
@@ -60,19 +60,21 @@ let JS = {
|
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
62
|
|
|
63
|
-
exec_transition(eventType, phxEvent, view, sourceEl, {time, to,
|
|
63
|
+
exec_transition(eventType, phxEvent, view, sourceEl, {time, to, transition}){
|
|
64
64
|
let els = to ? DOM.all(document, to) : [sourceEl]
|
|
65
|
+
let [transition_start, running, transition_end] = transition
|
|
65
66
|
els.forEach(el => {
|
|
66
|
-
this.addOrRemoveClasses(el,
|
|
67
|
-
|
|
67
|
+
let onStart = () => this.addOrRemoveClasses(el, transition_start.concat(running), [])
|
|
68
|
+
let onDone = () => this.addOrRemoveClasses(el, transition_end, transition_start.concat(running))
|
|
69
|
+
view.transition(time, onStart, onDone)
|
|
68
70
|
})
|
|
69
71
|
},
|
|
70
72
|
|
|
71
73
|
exec_toggle(eventType, phxEvent, view, sourceEl, {to, display, ins, outs, time}){
|
|
72
74
|
if(to){
|
|
73
|
-
DOM.all(document, to, el => this.toggle(eventType, view, el, display, ins
|
|
75
|
+
DOM.all(document, to, el => this.toggle(eventType, view, el, display, ins, outs, time))
|
|
74
76
|
} else {
|
|
75
|
-
this.toggle(eventType, view, sourceEl, display, ins
|
|
77
|
+
this.toggle(eventType, view, sourceEl, display, ins, outs, time)
|
|
76
78
|
}
|
|
77
79
|
},
|
|
78
80
|
|
|
@@ -95,37 +97,45 @@ let JS = {
|
|
|
95
97
|
// utils for commands
|
|
96
98
|
|
|
97
99
|
show(eventType, view, el, display, transition, time){
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this.toggle(eventType, view, el, display, transition, [], time)
|
|
101
|
-
} else if(!isVisible){
|
|
102
|
-
this.toggle(eventType, view, el, display, [], [], null)
|
|
100
|
+
if(!this.isVisible(el)){
|
|
101
|
+
this.toggle(eventType, view, el, display, transition, null, time)
|
|
103
102
|
}
|
|
104
103
|
},
|
|
105
104
|
|
|
106
105
|
hide(eventType, view, el, display, transition, time){
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.toggle(eventType, view, el, display, [], transition, time)
|
|
110
|
-
} else if(isVisible){
|
|
111
|
-
this.toggle(eventType, view, el, display, [], [], time)
|
|
106
|
+
if(this.isVisible(el)){
|
|
107
|
+
this.toggle(eventType, view, el, display, null, transition, time)
|
|
112
108
|
}
|
|
113
109
|
},
|
|
114
110
|
|
|
115
|
-
toggle(eventType, view, el, display,
|
|
116
|
-
|
|
111
|
+
toggle(eventType, view, el, display, ins, outs, time){
|
|
112
|
+
let [inClasses, inStartClasses, inEndClasses] = ins || [[], [], []]
|
|
113
|
+
let [outClasses, outStartClasses, outEndClasses] = outs || [[], [], []]
|
|
114
|
+
if(inClasses.length > 0 || outClasses.length > 0){
|
|
117
115
|
if(this.isVisible(el)){
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
let onStart = () => {
|
|
117
|
+
this.addOrRemoveClasses(el, outStartClasses, inClasses.concat(inStartClasses).concat(inEndClasses))
|
|
118
|
+
window.requestAnimationFrame(() => {
|
|
119
|
+
this.addOrRemoveClasses(el, outClasses, [])
|
|
120
|
+
window.requestAnimationFrame(() => this.addOrRemoveClasses(el, outEndClasses, outStartClasses))
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
view.transition(time, onStart, () => {
|
|
124
|
+
this.addOrRemoveClasses(el, [], outClasses.concat(outEndClasses))
|
|
120
125
|
DOM.putSticky(el, "toggle", currentEl => currentEl.style.display = "none")
|
|
121
|
-
this.addOrRemoveClasses(el, [], out_classes)
|
|
122
126
|
})
|
|
123
127
|
} else {
|
|
124
128
|
if(eventType === "remove"){ return }
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
let onStart = () => {
|
|
130
|
+
this.addOrRemoveClasses(el, inStartClasses, outClasses.concat(outStartClasses).concat(outEndClasses))
|
|
131
|
+
DOM.putSticky(el, "toggle", currentEl => currentEl.style.display = (display || "block"))
|
|
132
|
+
window.requestAnimationFrame(() => {
|
|
133
|
+
this.addOrRemoveClasses(el, inClasses, [])
|
|
134
|
+
window.requestAnimationFrame(() => this.addOrRemoveClasses(el, inEndClasses, inStartClasses))
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
view.transition(time, onStart, () => {
|
|
138
|
+
this.addOrRemoveClasses(el, [], inClasses.concat(inEndClasses))
|
|
129
139
|
})
|
|
130
140
|
}
|
|
131
141
|
} else {
|
|
@@ -135,9 +145,11 @@ let JS = {
|
|
|
135
145
|
},
|
|
136
146
|
|
|
137
147
|
addOrRemoveClasses(el, adds, removes, transition, time, view){
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
148
|
+
let [transition_run, transition_start, transition_end] = transition || [[], [], []]
|
|
149
|
+
if(transition_run.length > 0){
|
|
150
|
+
let onStart = () => this.addOrRemoveClasses(el, transition_start.concat(transition_run), [])
|
|
151
|
+
let onDone = () => this.addOrRemoveClasses(el, adds.concat(transition_end), removes.concat(transition_run).concat(transition_start))
|
|
152
|
+
return view.transition(time, onStart, onDone)
|
|
141
153
|
}
|
|
142
154
|
window.requestAnimationFrame(() => {
|
|
143
155
|
let [prevAdds, prevRemoves] = DOM.getSticky(el, "classes", [[], []])
|
|
@@ -161,8 +173,8 @@ let JS = {
|
|
|
161
173
|
return !(style.opacity === 0 || style.display === "none")
|
|
162
174
|
},
|
|
163
175
|
|
|
164
|
-
isToggledOut(el,
|
|
165
|
-
return !this.isVisible(el) || this.hasAllClasses(el,
|
|
176
|
+
isToggledOut(el, outClasses){
|
|
177
|
+
return !this.isVisible(el) || this.hasAllClasses(el, outClasses)
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
|
|
@@ -232,8 +232,8 @@ export default class LiveSocket {
|
|
|
232
232
|
this.transitions.after(callback)
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
transition(time, onDone = function(){}){
|
|
236
|
-
this.transitions.addTransition(time, onDone)
|
|
235
|
+
transition(time, onStart, onDone = function(){}){
|
|
236
|
+
this.transitions.addTransition(time, onStart, onDone)
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
onChannel(channel, event, cb){
|
|
@@ -369,7 +369,7 @@ export default class LiveSocket {
|
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
owner(childEl, callback){
|
|
372
|
-
let view = maybe(childEl.closest(PHX_VIEW_SELECTOR), el => this.getViewByEl(el))
|
|
372
|
+
let view = maybe(childEl.closest(PHX_VIEW_SELECTOR), el => this.getViewByEl(el)) || this.main
|
|
373
373
|
if(view){ callback(view) }
|
|
374
374
|
}
|
|
375
375
|
|
|
@@ -775,7 +775,8 @@ class TransitionSet {
|
|
|
775
775
|
}
|
|
776
776
|
}
|
|
777
777
|
|
|
778
|
-
addTransition(time, onDone){
|
|
778
|
+
addTransition(time, onStart, onDone){
|
|
779
|
+
onStart()
|
|
779
780
|
let timer = setTimeout(() => {
|
|
780
781
|
this.transitions.delete(timer)
|
|
781
782
|
onDone()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
COMPONENTS,
|
|
3
3
|
DYNAMICS,
|
|
4
|
+
TEMPLATES,
|
|
4
5
|
EVENTS,
|
|
5
6
|
PHX_COMPONENT,
|
|
6
7
|
PHX_SKIP,
|
|
@@ -39,7 +40,7 @@ export default class Rendered {
|
|
|
39
40
|
recursiveToString(rendered, components = rendered[COMPONENTS], onlyCids){
|
|
40
41
|
onlyCids = onlyCids ? new Set(onlyCids) : null
|
|
41
42
|
let output = {buffer: "", components: components, onlyCids: onlyCids}
|
|
42
|
-
this.toOutputBuffer(rendered, output)
|
|
43
|
+
this.toOutputBuffer(rendered, null, output)
|
|
43
44
|
return output.buffer
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -66,7 +67,7 @@ export default class Rendered {
|
|
|
66
67
|
newc[cid] = this.cachedFindComponent(cid, newc[cid], oldc, newc, cache)
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
for(
|
|
70
|
+
for(let cid in newc){ oldc[cid] = newc[cid] }
|
|
70
71
|
diff[COMPONENTS] = newc
|
|
71
72
|
}
|
|
72
73
|
}
|
|
@@ -143,35 +144,46 @@ export default class Rendered {
|
|
|
143
144
|
|
|
144
145
|
isNewFingerprint(diff = {}){ return !!diff[STATIC] }
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
if(
|
|
147
|
+
templateStatic(part, templates){
|
|
148
|
+
if(typeof (part) === "number") {
|
|
149
|
+
return templates[part]
|
|
150
|
+
} else {
|
|
151
|
+
return part
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
toOutputBuffer(rendered, templates, output){
|
|
156
|
+
if(rendered[DYNAMICS]){ return this.comprehensionToBuffer(rendered, templates, output) }
|
|
148
157
|
let {[STATIC]: statics} = rendered
|
|
158
|
+
statics = this.templateStatic(statics, templates)
|
|
149
159
|
|
|
150
160
|
output.buffer += statics[0]
|
|
151
161
|
for(let i = 1; i < statics.length; i++){
|
|
152
|
-
this.dynamicToBuffer(rendered[i - 1], output)
|
|
162
|
+
this.dynamicToBuffer(rendered[i - 1], templates, output)
|
|
153
163
|
output.buffer += statics[i]
|
|
154
164
|
}
|
|
155
165
|
}
|
|
156
166
|
|
|
157
|
-
comprehensionToBuffer(rendered, output){
|
|
167
|
+
comprehensionToBuffer(rendered, templates, output){
|
|
158
168
|
let {[DYNAMICS]: dynamics, [STATIC]: statics} = rendered
|
|
169
|
+
statics = this.templateStatic(statics, templates)
|
|
170
|
+
let compTemplates = rendered[TEMPLATES]
|
|
159
171
|
|
|
160
172
|
for(let d = 0; d < dynamics.length; d++){
|
|
161
173
|
let dynamic = dynamics[d]
|
|
162
174
|
output.buffer += statics[0]
|
|
163
175
|
for(let i = 1; i < statics.length; i++){
|
|
164
|
-
this.dynamicToBuffer(dynamic[i - 1], output)
|
|
176
|
+
this.dynamicToBuffer(dynamic[i - 1], compTemplates, output)
|
|
165
177
|
output.buffer += statics[i]
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
}
|
|
169
181
|
|
|
170
|
-
dynamicToBuffer(rendered, output){
|
|
182
|
+
dynamicToBuffer(rendered, templates, output){
|
|
171
183
|
if(typeof (rendered) === "number"){
|
|
172
184
|
output.buffer += this.recursiveCIDToString(output.components, rendered, output.onlyCids)
|
|
173
185
|
} else if(isObject(rendered)){
|
|
174
|
-
this.toOutputBuffer(rendered, output)
|
|
186
|
+
this.toOutputBuffer(rendered, templates, output)
|
|
175
187
|
} else {
|
|
176
188
|
output.buffer += rendered
|
|
177
189
|
}
|
|
@@ -6,7 +6,10 @@ import EntryUploader from "./entry_uploader"
|
|
|
6
6
|
|
|
7
7
|
export let logError = (msg, obj) => console.error && console.error(msg, obj)
|
|
8
8
|
|
|
9
|
-
export let isCid = (cid) =>
|
|
9
|
+
export let isCid = (cid) => {
|
|
10
|
+
let type = typeof(cid)
|
|
11
|
+
return type === "number" || (type === "string" && /^(0|[1-9]\d*)$/.test(cid))
|
|
12
|
+
}
|
|
10
13
|
|
|
11
14
|
export function detectDuplicateIds(){
|
|
12
15
|
let ids = new Set()
|
|
@@ -190,8 +190,8 @@ export default class View {
|
|
|
190
190
|
this.liveSocket.log(this, kind, msgCallback)
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
transition(time, onDone = function(){}){
|
|
194
|
-
this.liveSocket.transition(time, onDone)
|
|
193
|
+
transition(time, onStart, onDone = function(){}){
|
|
194
|
+
this.liveSocket.transition(time, onStart, onDone)
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
withinTargets(phxTarget, callback){
|
|
@@ -199,7 +199,7 @@ export default class View {
|
|
|
199
199
|
return this.liveSocket.owner(phxTarget, view => callback(view, phxTarget))
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
if(
|
|
202
|
+
if(isCid(phxTarget)){
|
|
203
203
|
let targets = DOM.findComponentNodeList(this.el, phxTarget)
|
|
204
204
|
if(targets.length === 0){
|
|
205
205
|
logError(`no component found matching phx-target of ${phxTarget}`)
|
|
@@ -716,9 +716,12 @@ export default class View {
|
|
|
716
716
|
}
|
|
717
717
|
|
|
718
718
|
targetComponentID(target, targetCtx){
|
|
719
|
-
if(isCid(targetCtx)){
|
|
720
|
-
|
|
721
|
-
|
|
719
|
+
if(isCid(targetCtx)){ return targetCtx }
|
|
720
|
+
|
|
721
|
+
let cidOrSelector = target.getAttribute(this.binding("target"))
|
|
722
|
+
if(isCid(cidOrSelector)){
|
|
723
|
+
return parseInt(cidOrSelector)
|
|
724
|
+
} else if(targetCtx && cidOrSelector !== null){
|
|
722
725
|
return this.closestComponentID(targetCtx)
|
|
723
726
|
} else {
|
|
724
727
|
return null
|
|
@@ -1008,7 +1011,7 @@ export default class View {
|
|
|
1008
1011
|
.map(form => {
|
|
1009
1012
|
let newForm = template.content.querySelector(`form[id="${form.id}"][${phxChange}="${form.getAttribute(phxChange)}"]`)
|
|
1010
1013
|
if(newForm){
|
|
1011
|
-
return [form, newForm, this.
|
|
1014
|
+
return [form, newForm, this.targetComponentID(newForm)]
|
|
1012
1015
|
} else {
|
|
1013
1016
|
return [form, null, null]
|
|
1014
1017
|
}
|
package/package.json
CHANGED
|
@@ -88,6 +88,7 @@ var COMPONENTS = "c";
|
|
|
88
88
|
var EVENTS = "e";
|
|
89
89
|
var REPLY = "r";
|
|
90
90
|
var TITLE = "t";
|
|
91
|
+
var TEMPLATES = "p";
|
|
91
92
|
|
|
92
93
|
// js/phoenix_live_view/entry_uploader.js
|
|
93
94
|
var EntryUploader = class {
|
|
@@ -139,7 +140,10 @@ var EntryUploader = class {
|
|
|
139
140
|
|
|
140
141
|
// js/phoenix_live_view/utils.js
|
|
141
142
|
var logError = (msg, obj) => console.error && console.error(msg, obj);
|
|
142
|
-
var isCid = (cid) =>
|
|
143
|
+
var isCid = (cid) => {
|
|
144
|
+
let type = typeof cid;
|
|
145
|
+
return type === "number" || type === "string" && /^(0|[1-9]\d*)$/.test(cid);
|
|
146
|
+
};
|
|
143
147
|
function detectDuplicateIds() {
|
|
144
148
|
let ids = new Set();
|
|
145
149
|
let elems = document.querySelectorAll("*[id]");
|
|
@@ -1673,7 +1677,7 @@ var Rendered = class {
|
|
|
1673
1677
|
recursiveToString(rendered, components = rendered[COMPONENTS], onlyCids) {
|
|
1674
1678
|
onlyCids = onlyCids ? new Set(onlyCids) : null;
|
|
1675
1679
|
let output = { buffer: "", components, onlyCids };
|
|
1676
|
-
this.toOutputBuffer(rendered, output);
|
|
1680
|
+
this.toOutputBuffer(rendered, null, output);
|
|
1677
1681
|
return output.buffer;
|
|
1678
1682
|
}
|
|
1679
1683
|
componentCIDs(diff) {
|
|
@@ -1699,8 +1703,8 @@ var Rendered = class {
|
|
|
1699
1703
|
for (let cid in newc) {
|
|
1700
1704
|
newc[cid] = this.cachedFindComponent(cid, newc[cid], oldc, newc, cache);
|
|
1701
1705
|
}
|
|
1702
|
-
for (
|
|
1703
|
-
oldc[
|
|
1706
|
+
for (let cid in newc) {
|
|
1707
|
+
oldc[cid] = newc[cid];
|
|
1704
1708
|
}
|
|
1705
1709
|
diff[COMPONENTS] = newc;
|
|
1706
1710
|
}
|
|
@@ -1769,33 +1773,43 @@ var Rendered = class {
|
|
|
1769
1773
|
isNewFingerprint(diff = {}) {
|
|
1770
1774
|
return !!diff[STATIC];
|
|
1771
1775
|
}
|
|
1772
|
-
|
|
1776
|
+
templateStatic(part, templates) {
|
|
1777
|
+
if (typeof part === "number") {
|
|
1778
|
+
return templates[part];
|
|
1779
|
+
} else {
|
|
1780
|
+
return part;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
toOutputBuffer(rendered, templates, output) {
|
|
1773
1784
|
if (rendered[DYNAMICS]) {
|
|
1774
|
-
return this.comprehensionToBuffer(rendered, output);
|
|
1785
|
+
return this.comprehensionToBuffer(rendered, templates, output);
|
|
1775
1786
|
}
|
|
1776
1787
|
let { [STATIC]: statics } = rendered;
|
|
1788
|
+
statics = this.templateStatic(statics, templates);
|
|
1777
1789
|
output.buffer += statics[0];
|
|
1778
1790
|
for (let i = 1; i < statics.length; i++) {
|
|
1779
|
-
this.dynamicToBuffer(rendered[i - 1], output);
|
|
1791
|
+
this.dynamicToBuffer(rendered[i - 1], templates, output);
|
|
1780
1792
|
output.buffer += statics[i];
|
|
1781
1793
|
}
|
|
1782
1794
|
}
|
|
1783
|
-
comprehensionToBuffer(rendered, output) {
|
|
1795
|
+
comprehensionToBuffer(rendered, templates, output) {
|
|
1784
1796
|
let { [DYNAMICS]: dynamics, [STATIC]: statics } = rendered;
|
|
1797
|
+
statics = this.templateStatic(statics, templates);
|
|
1798
|
+
let compTemplates = rendered[TEMPLATES];
|
|
1785
1799
|
for (let d = 0; d < dynamics.length; d++) {
|
|
1786
1800
|
let dynamic = dynamics[d];
|
|
1787
1801
|
output.buffer += statics[0];
|
|
1788
1802
|
for (let i = 1; i < statics.length; i++) {
|
|
1789
|
-
this.dynamicToBuffer(dynamic[i - 1], output);
|
|
1803
|
+
this.dynamicToBuffer(dynamic[i - 1], compTemplates, output);
|
|
1790
1804
|
output.buffer += statics[i];
|
|
1791
1805
|
}
|
|
1792
1806
|
}
|
|
1793
1807
|
}
|
|
1794
|
-
dynamicToBuffer(rendered, output) {
|
|
1808
|
+
dynamicToBuffer(rendered, templates, output) {
|
|
1795
1809
|
if (typeof rendered === "number") {
|
|
1796
1810
|
output.buffer += this.recursiveCIDToString(output.components, rendered, output.onlyCids);
|
|
1797
1811
|
} else if (isObject(rendered)) {
|
|
1798
|
-
this.toOutputBuffer(rendered, output);
|
|
1812
|
+
this.toOutputBuffer(rendered, templates, output);
|
|
1799
1813
|
} else {
|
|
1800
1814
|
output.buffer += rendered;
|
|
1801
1815
|
}
|
|
@@ -1981,18 +1995,20 @@ var JS = {
|
|
|
1981
1995
|
this.addOrRemoveClasses(sourceEl, [], names, transition, time, view);
|
|
1982
1996
|
}
|
|
1983
1997
|
},
|
|
1984
|
-
exec_transition(eventType, phxEvent, view, sourceEl, { time, to,
|
|
1998
|
+
exec_transition(eventType, phxEvent, view, sourceEl, { time, to, transition }) {
|
|
1985
1999
|
let els = to ? dom_default.all(document, to) : [sourceEl];
|
|
2000
|
+
let [transition_start, running, transition_end] = transition;
|
|
1986
2001
|
els.forEach((el) => {
|
|
1987
|
-
this.addOrRemoveClasses(el,
|
|
1988
|
-
|
|
2002
|
+
let onStart = () => this.addOrRemoveClasses(el, transition_start.concat(running), []);
|
|
2003
|
+
let onDone = () => this.addOrRemoveClasses(el, transition_end, transition_start.concat(running));
|
|
2004
|
+
view.transition(time, onStart, onDone);
|
|
1989
2005
|
});
|
|
1990
2006
|
},
|
|
1991
2007
|
exec_toggle(eventType, phxEvent, view, sourceEl, { to, display, ins, outs, time }) {
|
|
1992
2008
|
if (to) {
|
|
1993
|
-
dom_default.all(document, to, (el) => this.toggle(eventType, view, el, display, ins
|
|
2009
|
+
dom_default.all(document, to, (el) => this.toggle(eventType, view, el, display, ins, outs, time));
|
|
1994
2010
|
} else {
|
|
1995
|
-
this.toggle(eventType, view, sourceEl, display, ins
|
|
2011
|
+
this.toggle(eventType, view, sourceEl, display, ins, outs, time);
|
|
1996
2012
|
}
|
|
1997
2013
|
},
|
|
1998
2014
|
exec_show(eventType, phxEvent, view, sourceEl, { to, display, transition, time }) {
|
|
@@ -2010,37 +2026,45 @@ var JS = {
|
|
|
2010
2026
|
}
|
|
2011
2027
|
},
|
|
2012
2028
|
show(eventType, view, el, display, transition, time) {
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
this.toggle(eventType, view, el, display, transition, [], time);
|
|
2016
|
-
} else if (!isVisible) {
|
|
2017
|
-
this.toggle(eventType, view, el, display, [], [], null);
|
|
2029
|
+
if (!this.isVisible(el)) {
|
|
2030
|
+
this.toggle(eventType, view, el, display, transition, null, time);
|
|
2018
2031
|
}
|
|
2019
2032
|
},
|
|
2020
2033
|
hide(eventType, view, el, display, transition, time) {
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
this.toggle(eventType, view, el, display, [], transition, time);
|
|
2024
|
-
} else if (isVisible) {
|
|
2025
|
-
this.toggle(eventType, view, el, display, [], [], time);
|
|
2034
|
+
if (this.isVisible(el)) {
|
|
2035
|
+
this.toggle(eventType, view, el, display, null, transition, time);
|
|
2026
2036
|
}
|
|
2027
2037
|
},
|
|
2028
|
-
toggle(eventType, view, el, display,
|
|
2029
|
-
|
|
2038
|
+
toggle(eventType, view, el, display, ins, outs, time) {
|
|
2039
|
+
let [inClasses, inStartClasses, inEndClasses] = ins || [[], [], []];
|
|
2040
|
+
let [outClasses, outStartClasses, outEndClasses] = outs || [[], [], []];
|
|
2041
|
+
if (inClasses.length > 0 || outClasses.length > 0) {
|
|
2030
2042
|
if (this.isVisible(el)) {
|
|
2031
|
-
|
|
2032
|
-
|
|
2043
|
+
let onStart = () => {
|
|
2044
|
+
this.addOrRemoveClasses(el, outStartClasses, inClasses.concat(inStartClasses).concat(inEndClasses));
|
|
2045
|
+
window.requestAnimationFrame(() => {
|
|
2046
|
+
this.addOrRemoveClasses(el, outClasses, []);
|
|
2047
|
+
window.requestAnimationFrame(() => this.addOrRemoveClasses(el, outEndClasses, outStartClasses));
|
|
2048
|
+
});
|
|
2049
|
+
};
|
|
2050
|
+
view.transition(time, onStart, () => {
|
|
2051
|
+
this.addOrRemoveClasses(el, [], outClasses.concat(outEndClasses));
|
|
2033
2052
|
dom_default.putSticky(el, "toggle", (currentEl) => currentEl.style.display = "none");
|
|
2034
|
-
this.addOrRemoveClasses(el, [], out_classes);
|
|
2035
2053
|
});
|
|
2036
2054
|
} else {
|
|
2037
2055
|
if (eventType === "remove") {
|
|
2038
2056
|
return;
|
|
2039
2057
|
}
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2058
|
+
let onStart = () => {
|
|
2059
|
+
this.addOrRemoveClasses(el, inStartClasses, outClasses.concat(outStartClasses).concat(outEndClasses));
|
|
2060
|
+
dom_default.putSticky(el, "toggle", (currentEl) => currentEl.style.display = display || "block");
|
|
2061
|
+
window.requestAnimationFrame(() => {
|
|
2062
|
+
this.addOrRemoveClasses(el, inClasses, []);
|
|
2063
|
+
window.requestAnimationFrame(() => this.addOrRemoveClasses(el, inEndClasses, inStartClasses));
|
|
2064
|
+
});
|
|
2065
|
+
};
|
|
2066
|
+
view.transition(time, onStart, () => {
|
|
2067
|
+
this.addOrRemoveClasses(el, [], inClasses.concat(inEndClasses));
|
|
2044
2068
|
});
|
|
2045
2069
|
}
|
|
2046
2070
|
} else {
|
|
@@ -2049,9 +2073,11 @@ var JS = {
|
|
|
2049
2073
|
}
|
|
2050
2074
|
},
|
|
2051
2075
|
addOrRemoveClasses(el, adds, removes, transition, time, view) {
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2076
|
+
let [transition_run, transition_start, transition_end] = transition || [[], [], []];
|
|
2077
|
+
if (transition_run.length > 0) {
|
|
2078
|
+
let onStart = () => this.addOrRemoveClasses(el, transition_start.concat(transition_run), []);
|
|
2079
|
+
let onDone = () => this.addOrRemoveClasses(el, adds.concat(transition_end), removes.concat(transition_run).concat(transition_start));
|
|
2080
|
+
return view.transition(time, onStart, onDone);
|
|
2055
2081
|
}
|
|
2056
2082
|
window.requestAnimationFrame(() => {
|
|
2057
2083
|
let [prevAdds, prevRemoves] = dom_default.getSticky(el, "classes", [[], []]);
|
|
@@ -2073,8 +2099,8 @@ var JS = {
|
|
|
2073
2099
|
let style = window.getComputedStyle(el);
|
|
2074
2100
|
return !(style.opacity === 0 || style.display === "none");
|
|
2075
2101
|
},
|
|
2076
|
-
isToggledOut(el,
|
|
2077
|
-
return !this.isVisible(el) || this.hasAllClasses(el,
|
|
2102
|
+
isToggledOut(el, outClasses) {
|
|
2103
|
+
return !this.isVisible(el) || this.hasAllClasses(el, outClasses);
|
|
2078
2104
|
}
|
|
2079
2105
|
};
|
|
2080
2106
|
var js_default = JS;
|
|
@@ -2215,15 +2241,15 @@ var View = class {
|
|
|
2215
2241
|
log(kind, msgCallback) {
|
|
2216
2242
|
this.liveSocket.log(this, kind, msgCallback);
|
|
2217
2243
|
}
|
|
2218
|
-
transition(time, onDone = function() {
|
|
2244
|
+
transition(time, onStart, onDone = function() {
|
|
2219
2245
|
}) {
|
|
2220
|
-
this.liveSocket.transition(time, onDone);
|
|
2246
|
+
this.liveSocket.transition(time, onStart, onDone);
|
|
2221
2247
|
}
|
|
2222
2248
|
withinTargets(phxTarget, callback) {
|
|
2223
2249
|
if (phxTarget instanceof HTMLElement || phxTarget instanceof SVGElement) {
|
|
2224
2250
|
return this.liveSocket.owner(phxTarget, (view) => callback(view, phxTarget));
|
|
2225
2251
|
}
|
|
2226
|
-
if (
|
|
2252
|
+
if (isCid(phxTarget)) {
|
|
2227
2253
|
let targets = dom_default.findComponentNodeList(this.el, phxTarget);
|
|
2228
2254
|
if (targets.length === 0) {
|
|
2229
2255
|
logError(`no component found matching phx-target of ${phxTarget}`);
|
|
@@ -2727,7 +2753,11 @@ var View = class {
|
|
|
2727
2753
|
targetComponentID(target, targetCtx) {
|
|
2728
2754
|
if (isCid(targetCtx)) {
|
|
2729
2755
|
return targetCtx;
|
|
2730
|
-
}
|
|
2756
|
+
}
|
|
2757
|
+
let cidOrSelector = target.getAttribute(this.binding("target"));
|
|
2758
|
+
if (isCid(cidOrSelector)) {
|
|
2759
|
+
return parseInt(cidOrSelector);
|
|
2760
|
+
} else if (targetCtx && cidOrSelector !== null) {
|
|
2731
2761
|
return this.closestComponentID(targetCtx);
|
|
2732
2762
|
} else {
|
|
2733
2763
|
return null;
|
|
@@ -3003,7 +3033,7 @@ var View = class {
|
|
|
3003
3033
|
return dom_default.all(this.el, `form[${phxChange}]`).filter((form) => form.id && this.ownsElement(form)).filter((form) => form.elements.length > 0).filter((form) => form.getAttribute(this.binding(PHX_AUTO_RECOVER)) !== "ignore").map((form) => {
|
|
3004
3034
|
let newForm = template.content.querySelector(`form[id="${form.id}"][${phxChange}="${form.getAttribute(phxChange)}"]`);
|
|
3005
3035
|
if (newForm) {
|
|
3006
|
-
return [form, newForm, this.
|
|
3036
|
+
return [form, newForm, this.targetComponentID(newForm)];
|
|
3007
3037
|
} else {
|
|
3008
3038
|
return [form, null, null];
|
|
3009
3039
|
}
|
|
@@ -3165,9 +3195,9 @@ var LiveSocket = class {
|
|
|
3165
3195
|
requestDOMUpdate(callback) {
|
|
3166
3196
|
this.transitions.after(callback);
|
|
3167
3197
|
}
|
|
3168
|
-
transition(time, onDone = function() {
|
|
3198
|
+
transition(time, onStart, onDone = function() {
|
|
3169
3199
|
}) {
|
|
3170
|
-
this.transitions.addTransition(time, onDone);
|
|
3200
|
+
this.transitions.addTransition(time, onStart, onDone);
|
|
3171
3201
|
}
|
|
3172
3202
|
onChannel(channel, event, cb) {
|
|
3173
3203
|
channel.on(event, (data) => {
|
|
@@ -3303,7 +3333,7 @@ var LiveSocket = class {
|
|
|
3303
3333
|
return view;
|
|
3304
3334
|
}
|
|
3305
3335
|
owner(childEl, callback) {
|
|
3306
|
-
let view = maybe(childEl.closest(PHX_VIEW_SELECTOR), (el) => this.getViewByEl(el));
|
|
3336
|
+
let view = maybe(childEl.closest(PHX_VIEW_SELECTOR), (el) => this.getViewByEl(el)) || this.main;
|
|
3307
3337
|
if (view) {
|
|
3308
3338
|
callback(view);
|
|
3309
3339
|
}
|
|
@@ -3713,7 +3743,8 @@ var TransitionSet = class {
|
|
|
3713
3743
|
this.pushPendingOp(callback);
|
|
3714
3744
|
}
|
|
3715
3745
|
}
|
|
3716
|
-
addTransition(time, onDone) {
|
|
3746
|
+
addTransition(time, onStart, onDone) {
|
|
3747
|
+
onStart();
|
|
3717
3748
|
let timer = setTimeout(() => {
|
|
3718
3749
|
this.transitions.delete(timer);
|
|
3719
3750
|
onDone();
|