phoenix_live_view 0.20.0 → 0.20.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.
- package/README.md +10 -9
- package/assets/js/phoenix_live_view/aria.js +1 -1
- package/assets/js/phoenix_live_view/constants.js +3 -1
- package/assets/js/phoenix_live_view/dom.js +9 -4
- package/assets/js/phoenix_live_view/dom_patch.js +35 -45
- package/assets/js/phoenix_live_view/entry_uploader.js +1 -0
- package/assets/js/phoenix_live_view/js.js +10 -0
- package/assets/js/phoenix_live_view/live_socket.js +9 -7
- package/assets/js/phoenix_live_view/live_uploader.js +1 -0
- package/assets/js/phoenix_live_view/rendered.js +216 -71
- package/assets/js/phoenix_live_view/upload_entry.js +2 -1
- package/assets/js/phoenix_live_view/view.js +23 -10
- package/assets/js/phoenix_live_view/view_hook.js +4 -2
- package/assets/package.json +2 -2
- package/package.json +1 -1
- package/priv/static/phoenix_live_view.cjs.js +266 -131
- package/priv/static/phoenix_live_view.cjs.js.map +2 -2
- package/priv/static/phoenix_live_view.esm.js +266 -131
- package/priv/static/phoenix_live_view.esm.js.map +2 -2
- package/priv/static/phoenix_live_view.js +266 -131
- package/priv/static/phoenix_live_view.min.js +5 -10
|
@@ -69,6 +69,7 @@ var LiveView = (() => {
|
|
|
69
69
|
var PHX_ACTIVE_ENTRY_REFS = "data-phx-active-refs";
|
|
70
70
|
var PHX_LIVE_FILE_UPDATED = "phx:live-file:updated";
|
|
71
71
|
var PHX_SKIP = "data-phx-skip";
|
|
72
|
+
var PHX_MAGIC_ID = "data-phx-id";
|
|
72
73
|
var PHX_PRUNE = "data-phx-prune";
|
|
73
74
|
var PHX_PAGE_LOADING = "page-loading";
|
|
74
75
|
var PHX_CONNECTED_CLASS = "phx-connected";
|
|
@@ -123,6 +124,7 @@ var LiveView = (() => {
|
|
|
123
124
|
};
|
|
124
125
|
var DYNAMICS = "d";
|
|
125
126
|
var STATIC = "s";
|
|
127
|
+
var ROOT = "r";
|
|
126
128
|
var COMPONENTS = "c";
|
|
127
129
|
var EVENTS = "e";
|
|
128
130
|
var REPLY = "r";
|
|
@@ -145,6 +147,7 @@ var LiveView = (() => {
|
|
|
145
147
|
if (this.errored) {
|
|
146
148
|
return;
|
|
147
149
|
}
|
|
150
|
+
this.uploadChannel.leave();
|
|
148
151
|
this.errored = true;
|
|
149
152
|
clearTimeout(this.chunkTimer);
|
|
150
153
|
this.entry.error(reason);
|
|
@@ -564,12 +567,17 @@ var LiveView = (() => {
|
|
|
564
567
|
el.setAttribute("data-phx-hook", "Phoenix.InfiniteScroll");
|
|
565
568
|
}
|
|
566
569
|
},
|
|
567
|
-
maybeHideFeedback(container,
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
if (input.
|
|
571
|
-
feedbacks.push(input.name
|
|
570
|
+
maybeHideFeedback(container, inputs, phxFeedbackFor) {
|
|
571
|
+
let feedbacks = [];
|
|
572
|
+
inputs.forEach((input) => {
|
|
573
|
+
if (!(this.private(input, PHX_HAS_FOCUSED) || this.private(input, PHX_HAS_SUBMITTED))) {
|
|
574
|
+
feedbacks.push(input.name);
|
|
575
|
+
if (input.name.endsWith("[]")) {
|
|
576
|
+
feedbacks.push(input.name.slice(0, -2));
|
|
577
|
+
}
|
|
572
578
|
}
|
|
579
|
+
});
|
|
580
|
+
if (feedbacks.length > 0) {
|
|
573
581
|
let selector = feedbacks.map((f) => `[${phxFeedbackFor}="${f}"]`).join(", ");
|
|
574
582
|
DOM.all(container, selector, (el) => el.classList.add(PHX_NO_FEEDBACK_CLASS));
|
|
575
583
|
}
|
|
@@ -856,7 +864,8 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
856
864
|
relative_path: this.file.webkitRelativePath,
|
|
857
865
|
size: this.file.size,
|
|
858
866
|
type: this.file.type,
|
|
859
|
-
ref: this.ref
|
|
867
|
+
ref: this.ref,
|
|
868
|
+
meta: typeof this.file.meta === "function" ? this.file.meta() : void 0
|
|
860
869
|
};
|
|
861
870
|
}
|
|
862
871
|
uploader(uploaders) {
|
|
@@ -913,6 +922,9 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
913
922
|
entry.relative_path = file.webkitRelativePath;
|
|
914
923
|
entry.type = file.type;
|
|
915
924
|
entry.size = file.size;
|
|
925
|
+
if (typeof file.meta === "function") {
|
|
926
|
+
entry.meta = file.meta();
|
|
927
|
+
}
|
|
916
928
|
fileData[uploadRef].push(entry);
|
|
917
929
|
});
|
|
918
930
|
return fileData;
|
|
@@ -1002,7 +1014,7 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
1002
1014
|
return classes.find((name) => instance instanceof name);
|
|
1003
1015
|
},
|
|
1004
1016
|
isFocusable(el, interactiveOnly) {
|
|
1005
|
-
return el instanceof HTMLAnchorElement && el.rel !== "ignore" || el instanceof HTMLAreaElement && el.href !== void 0 || !el.disabled && this.anyOf(el, [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLButtonElement]) || el instanceof HTMLIFrameElement || (el.tabIndex > 0 || !interactiveOnly && el.
|
|
1017
|
+
return el instanceof HTMLAnchorElement && el.rel !== "ignore" || el instanceof HTMLAreaElement && el.href !== void 0 || !el.disabled && this.anyOf(el, [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLButtonElement]) || el instanceof HTMLIFrameElement || (el.tabIndex > 0 || !interactiveOnly && el.getAttribute("tabindex") !== null && el.getAttribute("aria-hidden") !== "true");
|
|
1006
1018
|
},
|
|
1007
1019
|
attemptFocus(el, interactiveOnly) {
|
|
1008
1020
|
if (this.isFocusable(el, interactiveOnly)) {
|
|
@@ -1578,7 +1590,7 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
1578
1590
|
}
|
|
1579
1591
|
}
|
|
1580
1592
|
function morphChildren(fromEl, toEl) {
|
|
1581
|
-
var skipFrom = skipFromChildren(fromEl);
|
|
1593
|
+
var skipFrom = skipFromChildren(fromEl, toEl);
|
|
1582
1594
|
var curToNodeChild = toEl.firstChild;
|
|
1583
1595
|
var curFromNodeChild = fromEl.firstChild;
|
|
1584
1596
|
var curToNodeKey;
|
|
@@ -1781,7 +1793,7 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
1781
1793
|
el.setAttribute(PHX_PRUNE, "");
|
|
1782
1794
|
});
|
|
1783
1795
|
}
|
|
1784
|
-
perform() {
|
|
1796
|
+
perform(isJoinPatch) {
|
|
1785
1797
|
let { view, liveSocket, container, html } = this;
|
|
1786
1798
|
let targetContainer = this.isCIDPatch() ? this.targetCIDContainer(html) : container;
|
|
1787
1799
|
if (this.isCIDPatch() && !targetContainer) {
|
|
@@ -1800,19 +1812,18 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
1800
1812
|
let updates = [];
|
|
1801
1813
|
let appendPrependUpdates = [];
|
|
1802
1814
|
let externalFormTriggered = null;
|
|
1803
|
-
let diffHTML = liveSocket.time("premorph container prep", () => {
|
|
1804
|
-
return this.buildDiffHTML(container, html, phxUpdate, targetContainer);
|
|
1805
|
-
});
|
|
1806
1815
|
this.trackBefore("added", container);
|
|
1807
1816
|
this.trackBefore("updated", container, container);
|
|
1808
1817
|
liveSocket.time("morphdom", () => {
|
|
1809
1818
|
this.streams.forEach(([ref, inserts, deleteIds, reset]) => {
|
|
1810
1819
|
Object.entries(inserts).forEach(([key, [streamAt, limit]]) => {
|
|
1811
|
-
this.streamInserts[key] = { ref, streamAt, limit };
|
|
1820
|
+
this.streamInserts[key] = { ref, streamAt, limit, resetKept: false };
|
|
1812
1821
|
});
|
|
1813
1822
|
if (reset !== void 0) {
|
|
1814
1823
|
dom_default.all(container, `[${PHX_STREAM_REF}="${ref}"]`, (child) => {
|
|
1815
|
-
if (
|
|
1824
|
+
if (inserts[child.id]) {
|
|
1825
|
+
this.streamInserts[child.id].resetKept = true;
|
|
1826
|
+
} else {
|
|
1816
1827
|
this.removeStreamChildElement(child);
|
|
1817
1828
|
}
|
|
1818
1829
|
});
|
|
@@ -1824,10 +1835,16 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
1824
1835
|
}
|
|
1825
1836
|
});
|
|
1826
1837
|
});
|
|
1827
|
-
morphdom_esm_default(targetContainer,
|
|
1838
|
+
morphdom_esm_default(targetContainer, html, {
|
|
1828
1839
|
childrenOnly: targetContainer.getAttribute(PHX_COMPONENT) === null,
|
|
1829
1840
|
getNodeKey: (node) => {
|
|
1830
|
-
|
|
1841
|
+
if (dom_default.isPhxDestroyed(node)) {
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
if (isJoinPatch) {
|
|
1845
|
+
return node.id;
|
|
1846
|
+
}
|
|
1847
|
+
return node.id || node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
|
|
1831
1848
|
},
|
|
1832
1849
|
skipFromChildren: (from) => {
|
|
1833
1850
|
return from.getAttribute(phxUpdate) === PHX_STREAM;
|
|
@@ -1884,6 +1901,25 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
1884
1901
|
}
|
|
1885
1902
|
added.push(el);
|
|
1886
1903
|
},
|
|
1904
|
+
onBeforeElChildrenUpdated: (fromEl, toEl) => {
|
|
1905
|
+
if (fromEl.getAttribute(phxUpdate) === PHX_STREAM) {
|
|
1906
|
+
let toIds = Array.from(toEl.children).map((child) => child.id);
|
|
1907
|
+
Array.from(fromEl.children).filter((child) => {
|
|
1908
|
+
let { resetKept } = this.getStreamInsert(child);
|
|
1909
|
+
return resetKept;
|
|
1910
|
+
}).sort((a, b) => {
|
|
1911
|
+
let aIdx = toIds.indexOf(a.id);
|
|
1912
|
+
let bIdx = toIds.indexOf(b.id);
|
|
1913
|
+
if (aIdx === bIdx) {
|
|
1914
|
+
return 0;
|
|
1915
|
+
} else if (aIdx < bIdx) {
|
|
1916
|
+
return -1;
|
|
1917
|
+
} else {
|
|
1918
|
+
return 1;
|
|
1919
|
+
}
|
|
1920
|
+
}).forEach((child) => fromEl.appendChild(child));
|
|
1921
|
+
}
|
|
1922
|
+
},
|
|
1887
1923
|
onNodeDiscarded: (el) => this.onNodeDiscarded(el),
|
|
1888
1924
|
onBeforeNodeDiscarded: (el) => {
|
|
1889
1925
|
if (el.getAttribute && el.getAttribute(PHX_PRUNE) !== null) {
|
|
@@ -1977,9 +2013,7 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
1977
2013
|
appendPrependUpdates.forEach((update) => update.perform());
|
|
1978
2014
|
});
|
|
1979
2015
|
}
|
|
1980
|
-
|
|
1981
|
-
dom_default.maybeHideFeedback(targetContainer, input, phxFeedbackFor);
|
|
1982
|
-
});
|
|
2016
|
+
dom_default.maybeHideFeedback(targetContainer, trackedInputs, phxFeedbackFor);
|
|
1983
2017
|
liveSocket.silenceEvents(() => dom_default.restoreFocus(focused, selectionStart, selectionEnd));
|
|
1984
2018
|
dom_default.dispatchEvent(document, "phx:update");
|
|
1985
2019
|
added.forEach((el) => this.trackAfter("added", el));
|
|
@@ -2058,7 +2092,7 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2058
2092
|
return this.cidPatch;
|
|
2059
2093
|
}
|
|
2060
2094
|
skipCIDSibling(el) {
|
|
2061
|
-
return el.nodeType === Node.ELEMENT_NODE && el.
|
|
2095
|
+
return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
|
|
2062
2096
|
}
|
|
2063
2097
|
targetCIDContainer(html) {
|
|
2064
2098
|
if (!this.isCIDPatch()) {
|
|
@@ -2071,35 +2105,125 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2071
2105
|
return first && first.parentNode;
|
|
2072
2106
|
}
|
|
2073
2107
|
}
|
|
2074
|
-
buildDiffHTML(container, html, phxUpdate, targetContainer) {
|
|
2075
|
-
let isCIDPatch = this.isCIDPatch();
|
|
2076
|
-
let isCIDWithSingleRoot = isCIDPatch && targetContainer.getAttribute(PHX_COMPONENT) === this.targetCID.toString();
|
|
2077
|
-
if (!isCIDPatch || isCIDWithSingleRoot) {
|
|
2078
|
-
return html;
|
|
2079
|
-
} else {
|
|
2080
|
-
let diffContainer = null;
|
|
2081
|
-
let template = document.createElement("template");
|
|
2082
|
-
diffContainer = dom_default.cloneNode(targetContainer);
|
|
2083
|
-
let [firstComponent, ...rest] = dom_default.findComponentNodeList(diffContainer, this.targetCID);
|
|
2084
|
-
template.innerHTML = html;
|
|
2085
|
-
rest.forEach((el) => el.remove());
|
|
2086
|
-
Array.from(diffContainer.childNodes).forEach((child) => {
|
|
2087
|
-
if (child.id && child.nodeType === Node.ELEMENT_NODE && child.getAttribute(PHX_COMPONENT) !== this.targetCID.toString()) {
|
|
2088
|
-
child.setAttribute(PHX_SKIP, "");
|
|
2089
|
-
child.innerHTML = "";
|
|
2090
|
-
}
|
|
2091
|
-
});
|
|
2092
|
-
Array.from(template.content.childNodes).forEach((el) => diffContainer.insertBefore(el, firstComponent));
|
|
2093
|
-
firstComponent.remove();
|
|
2094
|
-
return diffContainer.outerHTML;
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
2108
|
indexOf(parent, child) {
|
|
2098
2109
|
return Array.from(parent.children).indexOf(child);
|
|
2099
2110
|
}
|
|
2100
2111
|
};
|
|
2101
2112
|
|
|
2102
2113
|
// js/phoenix_live_view/rendered.js
|
|
2114
|
+
var VOID_TAGS = new Set([
|
|
2115
|
+
"area",
|
|
2116
|
+
"base",
|
|
2117
|
+
"br",
|
|
2118
|
+
"col",
|
|
2119
|
+
"command",
|
|
2120
|
+
"embed",
|
|
2121
|
+
"hr",
|
|
2122
|
+
"img",
|
|
2123
|
+
"input",
|
|
2124
|
+
"keygen",
|
|
2125
|
+
"link",
|
|
2126
|
+
"meta",
|
|
2127
|
+
"param",
|
|
2128
|
+
"source",
|
|
2129
|
+
"track",
|
|
2130
|
+
"wbr"
|
|
2131
|
+
]);
|
|
2132
|
+
var endingTagNameChars = new Set([">", "/", " ", "\n", " ", "\r"]);
|
|
2133
|
+
var quoteChars = new Set(["'", '"']);
|
|
2134
|
+
var modifyRoot = (html, attrs, clearInnerHTML) => {
|
|
2135
|
+
let i = 0;
|
|
2136
|
+
let insideComment = false;
|
|
2137
|
+
let beforeTag, afterTag, tag, tagNameEndsAt, id, newHTML;
|
|
2138
|
+
while (i < html.length) {
|
|
2139
|
+
let char = html.charAt(i);
|
|
2140
|
+
if (insideComment) {
|
|
2141
|
+
if (char === "-" && html.slice(i, i + 3) === "-->") {
|
|
2142
|
+
insideComment = false;
|
|
2143
|
+
i += 3;
|
|
2144
|
+
} else {
|
|
2145
|
+
i++;
|
|
2146
|
+
}
|
|
2147
|
+
} else if (char === "<" && html.slice(i, i + 4) === "<!--") {
|
|
2148
|
+
insideComment = true;
|
|
2149
|
+
i += 4;
|
|
2150
|
+
} else if (char === "<") {
|
|
2151
|
+
beforeTag = html.slice(0, i);
|
|
2152
|
+
let iAtOpen = i;
|
|
2153
|
+
i++;
|
|
2154
|
+
for (i; i < html.length; i++) {
|
|
2155
|
+
if (endingTagNameChars.has(html.charAt(i))) {
|
|
2156
|
+
break;
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
tagNameEndsAt = i;
|
|
2160
|
+
tag = html.slice(iAtOpen + 1, tagNameEndsAt);
|
|
2161
|
+
for (i; i < html.length; i++) {
|
|
2162
|
+
if (html.charAt(i) === ">") {
|
|
2163
|
+
break;
|
|
2164
|
+
}
|
|
2165
|
+
if (html.charAt(i) === "=") {
|
|
2166
|
+
let isId = html.slice(i - 3, i) === " id";
|
|
2167
|
+
i++;
|
|
2168
|
+
let char2 = html.charAt(i);
|
|
2169
|
+
if (quoteChars.has(char2)) {
|
|
2170
|
+
let attrStartsAt = i;
|
|
2171
|
+
i++;
|
|
2172
|
+
for (i; i < html.length; i++) {
|
|
2173
|
+
if (html.charAt(i) === char2) {
|
|
2174
|
+
break;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
if (isId) {
|
|
2178
|
+
id = html.slice(attrStartsAt + 1, i);
|
|
2179
|
+
break;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
break;
|
|
2185
|
+
} else {
|
|
2186
|
+
i++;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
if (!tag) {
|
|
2190
|
+
throw new Error(`malformed html ${html}`);
|
|
2191
|
+
}
|
|
2192
|
+
let closeAt = html.length - 1;
|
|
2193
|
+
insideComment = false;
|
|
2194
|
+
while (closeAt >= beforeTag.length + tag.length) {
|
|
2195
|
+
let char = html.charAt(closeAt);
|
|
2196
|
+
if (insideComment) {
|
|
2197
|
+
if (char === "-" && html.slice(closeAt - 3, closeAt) === "<!-") {
|
|
2198
|
+
insideComment = false;
|
|
2199
|
+
closeAt -= 4;
|
|
2200
|
+
} else {
|
|
2201
|
+
closeAt -= 1;
|
|
2202
|
+
}
|
|
2203
|
+
} else if (char === ">" && html.slice(closeAt - 2, closeAt) === "--") {
|
|
2204
|
+
insideComment = true;
|
|
2205
|
+
closeAt -= 3;
|
|
2206
|
+
} else if (char === ">") {
|
|
2207
|
+
break;
|
|
2208
|
+
} else {
|
|
2209
|
+
closeAt -= 1;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
afterTag = html.slice(closeAt + 1, html.length);
|
|
2213
|
+
let attrsStr = Object.keys(attrs).map((attr) => attrs[attr] === true ? attr : `${attr}="${attrs[attr]}"`).join(" ");
|
|
2214
|
+
if (clearInnerHTML) {
|
|
2215
|
+
let idAttrStr = id ? ` id="${id}"` : "";
|
|
2216
|
+
if (VOID_TAGS.has(tag)) {
|
|
2217
|
+
newHTML = `<${tag}${idAttrStr}${attrsStr === "" ? "" : " "}${attrsStr}/>`;
|
|
2218
|
+
} else {
|
|
2219
|
+
newHTML = `<${tag}${idAttrStr}${attrsStr === "" ? "" : " "}${attrsStr}></${tag}>`;
|
|
2220
|
+
}
|
|
2221
|
+
} else {
|
|
2222
|
+
let rest = html.slice(tagNameEndsAt, closeAt + 1);
|
|
2223
|
+
newHTML = `<${tag}${attrsStr === "" ? "" : " "}${attrsStr}${rest}`;
|
|
2224
|
+
}
|
|
2225
|
+
return [newHTML, beforeTag, afterTag];
|
|
2226
|
+
};
|
|
2103
2227
|
var Rendered = class {
|
|
2104
2228
|
static extract(diff) {
|
|
2105
2229
|
let { [REPLY]: reply, [EVENTS]: events, [TITLE]: title } = diff;
|
|
@@ -2111,19 +2235,20 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2111
2235
|
constructor(viewId, rendered) {
|
|
2112
2236
|
this.viewId = viewId;
|
|
2113
2237
|
this.rendered = {};
|
|
2238
|
+
this.magicId = 0;
|
|
2114
2239
|
this.mergeDiff(rendered);
|
|
2115
2240
|
}
|
|
2116
2241
|
parentViewId() {
|
|
2117
2242
|
return this.viewId;
|
|
2118
2243
|
}
|
|
2119
2244
|
toString(onlyCids) {
|
|
2120
|
-
let [str, streams] = this.recursiveToString(this.rendered, this.rendered[COMPONENTS], onlyCids);
|
|
2245
|
+
let [str, streams] = this.recursiveToString(this.rendered, this.rendered[COMPONENTS], onlyCids, true, {});
|
|
2121
2246
|
return [str, streams];
|
|
2122
2247
|
}
|
|
2123
|
-
recursiveToString(rendered, components = rendered[COMPONENTS], onlyCids) {
|
|
2248
|
+
recursiveToString(rendered, components = rendered[COMPONENTS], onlyCids, changeTracking, rootAttrs) {
|
|
2124
2249
|
onlyCids = onlyCids ? new Set(onlyCids) : null;
|
|
2125
2250
|
let output = { buffer: "", components, onlyCids, streams: new Set() };
|
|
2126
|
-
this.toOutputBuffer(rendered, null, output);
|
|
2251
|
+
this.toOutputBuffer(rendered, null, output, changeTracking, rootAttrs);
|
|
2127
2252
|
return [output.buffer, output.streams];
|
|
2128
2253
|
}
|
|
2129
2254
|
componentCIDs(diff) {
|
|
@@ -2168,10 +2293,10 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2168
2293
|
tdiff = oldc[-scid];
|
|
2169
2294
|
}
|
|
2170
2295
|
stat = tdiff[STATIC];
|
|
2171
|
-
ndiff = this.cloneMerge(tdiff, cdiff);
|
|
2296
|
+
ndiff = this.cloneMerge(tdiff, cdiff, true);
|
|
2172
2297
|
ndiff[STATIC] = stat;
|
|
2173
2298
|
} else {
|
|
2174
|
-
ndiff = cdiff[STATIC] !== void 0 ? cdiff : this.cloneMerge(oldc[cid]
|
|
2299
|
+
ndiff = cdiff[STATIC] !== void 0 || oldc[cid] === void 0 ? cdiff : this.cloneMerge(oldc[cid], cdiff, false);
|
|
2175
2300
|
}
|
|
2176
2301
|
cache[cid] = ndiff;
|
|
2177
2302
|
return ndiff;
|
|
@@ -2196,21 +2321,31 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2196
2321
|
target[key] = val;
|
|
2197
2322
|
}
|
|
2198
2323
|
}
|
|
2324
|
+
if (target[ROOT]) {
|
|
2325
|
+
target.newRender = true;
|
|
2326
|
+
}
|
|
2199
2327
|
}
|
|
2200
|
-
cloneMerge(target, source) {
|
|
2328
|
+
cloneMerge(target, source, pruneMagicId) {
|
|
2201
2329
|
let merged = __spreadValues(__spreadValues({}, target), source);
|
|
2202
2330
|
for (let key in merged) {
|
|
2203
2331
|
let val = source[key];
|
|
2204
2332
|
let targetVal = target[key];
|
|
2205
2333
|
if (isObject(val) && val[STATIC] === void 0 && isObject(targetVal)) {
|
|
2206
|
-
merged[key] = this.cloneMerge(targetVal, val);
|
|
2334
|
+
merged[key] = this.cloneMerge(targetVal, val, pruneMagicId);
|
|
2207
2335
|
}
|
|
2208
2336
|
}
|
|
2337
|
+
if (pruneMagicId) {
|
|
2338
|
+
delete merged.magicId;
|
|
2339
|
+
delete merged.newRender;
|
|
2340
|
+
} else if (target[ROOT]) {
|
|
2341
|
+
merged.newRender = true;
|
|
2342
|
+
}
|
|
2209
2343
|
return merged;
|
|
2210
2344
|
}
|
|
2211
2345
|
componentToString(cid) {
|
|
2212
|
-
let [str, streams] = this.recursiveCIDToString(this.rendered[COMPONENTS], cid, null
|
|
2213
|
-
|
|
2346
|
+
let [str, streams] = this.recursiveCIDToString(this.rendered[COMPONENTS], cid, null);
|
|
2347
|
+
let [strippedHTML, _before, _after] = modifyRoot(str, {});
|
|
2348
|
+
return [strippedHTML, streams];
|
|
2214
2349
|
}
|
|
2215
2350
|
pruneCIDs(cids) {
|
|
2216
2351
|
cids.forEach((cid) => delete this.rendered[COMPONENTS][cid]);
|
|
@@ -2228,17 +2363,46 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2228
2363
|
return part;
|
|
2229
2364
|
}
|
|
2230
2365
|
}
|
|
2231
|
-
|
|
2366
|
+
nextMagicID() {
|
|
2367
|
+
this.magicId++;
|
|
2368
|
+
return `${this.parentViewId()}-${this.magicId}`;
|
|
2369
|
+
}
|
|
2370
|
+
toOutputBuffer(rendered, templates, output, changeTracking, rootAttrs = {}) {
|
|
2232
2371
|
if (rendered[DYNAMICS]) {
|
|
2233
2372
|
return this.comprehensionToBuffer(rendered, templates, output);
|
|
2234
2373
|
}
|
|
2235
2374
|
let { [STATIC]: statics } = rendered;
|
|
2236
2375
|
statics = this.templateStatic(statics, templates);
|
|
2376
|
+
let isRoot = rendered[ROOT];
|
|
2377
|
+
let prevBuffer = output.buffer;
|
|
2378
|
+
if (isRoot) {
|
|
2379
|
+
output.buffer = "";
|
|
2380
|
+
}
|
|
2381
|
+
if (changeTracking && isRoot && !rendered.magicId) {
|
|
2382
|
+
rendered.newRender = true;
|
|
2383
|
+
rendered.magicId = this.nextMagicID();
|
|
2384
|
+
}
|
|
2237
2385
|
output.buffer += statics[0];
|
|
2238
2386
|
for (let i = 1; i < statics.length; i++) {
|
|
2239
|
-
this.dynamicToBuffer(rendered[i - 1], templates, output);
|
|
2387
|
+
this.dynamicToBuffer(rendered[i - 1], templates, output, changeTracking);
|
|
2240
2388
|
output.buffer += statics[i];
|
|
2241
2389
|
}
|
|
2390
|
+
if (isRoot) {
|
|
2391
|
+
let skip = false;
|
|
2392
|
+
let attrs;
|
|
2393
|
+
if (changeTracking || Object.keys(rootAttrs).length > 0) {
|
|
2394
|
+
skip = !rendered.newRender;
|
|
2395
|
+
attrs = __spreadValues({ [PHX_MAGIC_ID]: rendered.magicId }, rootAttrs);
|
|
2396
|
+
} else {
|
|
2397
|
+
attrs = rootAttrs;
|
|
2398
|
+
}
|
|
2399
|
+
if (skip) {
|
|
2400
|
+
attrs[PHX_SKIP] = true;
|
|
2401
|
+
}
|
|
2402
|
+
let [newRoot, commentBefore, commentAfter] = modifyRoot(output.buffer, attrs, skip);
|
|
2403
|
+
rendered.newRender = false;
|
|
2404
|
+
output.buffer = prevBuffer + commentBefore + newRoot + commentAfter;
|
|
2405
|
+
}
|
|
2242
2406
|
}
|
|
2243
2407
|
comprehensionToBuffer(rendered, templates, output) {
|
|
2244
2408
|
let { [DYNAMICS]: dynamics, [STATIC]: statics, [STREAM]: stream } = rendered;
|
|
@@ -2249,7 +2413,8 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2249
2413
|
let dynamic = dynamics[d];
|
|
2250
2414
|
output.buffer += statics[0];
|
|
2251
2415
|
for (let i = 1; i < statics.length; i++) {
|
|
2252
|
-
|
|
2416
|
+
let changeTracking = false;
|
|
2417
|
+
this.dynamicToBuffer(dynamic[i - 1], compTemplates, output, changeTracking);
|
|
2253
2418
|
output.buffer += statics[i];
|
|
2254
2419
|
}
|
|
2255
2420
|
}
|
|
@@ -2259,74 +2424,26 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
|
|
|
2259
2424
|
output.streams.add(stream);
|
|
2260
2425
|
}
|
|
2261
2426
|
}
|
|
2262
|
-
dynamicToBuffer(rendered, templates, output) {
|
|
2427
|
+
dynamicToBuffer(rendered, templates, output, changeTracking) {
|
|
2263
2428
|
if (typeof rendered === "number") {
|
|
2264
2429
|
let [str, streams] = this.recursiveCIDToString(output.components, rendered, output.onlyCids);
|
|
2265
2430
|
output.buffer += str;
|
|
2266
2431
|
output.streams = new Set([...output.streams, ...streams]);
|
|
2267
2432
|
} else if (isObject(rendered)) {
|
|
2268
|
-
this.toOutputBuffer(rendered, templates, output);
|
|
2433
|
+
this.toOutputBuffer(rendered, templates, output, changeTracking, {});
|
|
2269
2434
|
} else {
|
|
2270
2435
|
output.buffer += rendered;
|
|
2271
2436
|
}
|
|
2272
2437
|
}
|
|
2273
|
-
recursiveCIDToString(components, cid, onlyCids
|
|
2438
|
+
recursiveCIDToString(components, cid, onlyCids) {
|
|
2274
2439
|
let component = components[cid] || logError(`no component for CID ${cid}`, components);
|
|
2275
|
-
let
|
|
2276
|
-
let [html, streams] = this.recursiveToString(component, components, onlyCids);
|
|
2277
|
-
template.innerHTML = html;
|
|
2278
|
-
let container = template.content;
|
|
2440
|
+
let attrs = { [PHX_COMPONENT]: cid };
|
|
2279
2441
|
let skip = onlyCids && !onlyCids.has(cid);
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
child.setAttribute(PHX_COMPONENT, cid);
|
|
2286
|
-
if (!child.id) {
|
|
2287
|
-
child.id = `${this.parentViewId()}-${cid}-${i}`;
|
|
2288
|
-
}
|
|
2289
|
-
if (skip) {
|
|
2290
|
-
child.setAttribute(PHX_SKIP, "");
|
|
2291
|
-
child.innerHTML = "";
|
|
2292
|
-
}
|
|
2293
|
-
return [true, hasComponents];
|
|
2294
|
-
} else if (child.nodeType === Node.COMMENT_NODE) {
|
|
2295
|
-
if (!allowRootComments) {
|
|
2296
|
-
child.remove();
|
|
2297
|
-
}
|
|
2298
|
-
return [hasNodes, hasComponents];
|
|
2299
|
-
} else {
|
|
2300
|
-
if (child.nodeValue.trim() !== "") {
|
|
2301
|
-
logError(`only HTML element tags are allowed at the root of components.
|
|
2302
|
-
|
|
2303
|
-
got: "${child.nodeValue.trim()}"
|
|
2304
|
-
|
|
2305
|
-
within:
|
|
2306
|
-
`, template.innerHTML.trim());
|
|
2307
|
-
child.replaceWith(this.createSpan(child.nodeValue, cid));
|
|
2308
|
-
return [true, hasComponents];
|
|
2309
|
-
} else {
|
|
2310
|
-
child.remove();
|
|
2311
|
-
return [hasNodes, hasComponents];
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
}, [false, false]);
|
|
2315
|
-
if (!hasChildNodes && !hasChildComponents) {
|
|
2316
|
-
logError("expected at least one HTML element tag inside a component, but the component is empty:\n", template.innerHTML.trim());
|
|
2317
|
-
return [this.createSpan("", cid).outerHTML, streams];
|
|
2318
|
-
} else if (!hasChildNodes && hasChildComponents) {
|
|
2319
|
-
logError("expected at least one HTML element tag directly inside a component, but only subcomponents were found. A component must render at least one HTML tag directly inside itself.", template.innerHTML.trim());
|
|
2320
|
-
return [template.innerHTML, streams];
|
|
2321
|
-
} else {
|
|
2322
|
-
return [template.innerHTML, streams];
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
createSpan(text, cid) {
|
|
2326
|
-
let span = document.createElement("span");
|
|
2327
|
-
span.innerText = text;
|
|
2328
|
-
span.setAttribute(PHX_COMPONENT, cid);
|
|
2329
|
-
return span;
|
|
2442
|
+
component.newRender = !skip;
|
|
2443
|
+
component.magicId = `${this.parentViewId()}-c-${cid}`;
|
|
2444
|
+
let changeTracking = true;
|
|
2445
|
+
let [html, streams] = this.recursiveToString(component, components, onlyCids, changeTracking, attrs);
|
|
2446
|
+
return [html, streams];
|
|
2330
2447
|
}
|
|
2331
2448
|
};
|
|
2332
2449
|
|
|
@@ -2395,10 +2512,12 @@ within:
|
|
|
2395
2512
|
this.__listeners.delete(callbackRef);
|
|
2396
2513
|
}
|
|
2397
2514
|
upload(name, files) {
|
|
2398
|
-
return this.__view.dispatchUploads(name, files);
|
|
2515
|
+
return this.__view.dispatchUploads(null, name, files);
|
|
2399
2516
|
}
|
|
2400
2517
|
uploadTo(phxTarget, name, files) {
|
|
2401
|
-
return this.__view.withinTargets(phxTarget, (view) =>
|
|
2518
|
+
return this.__view.withinTargets(phxTarget, (view, targetCtx) => {
|
|
2519
|
+
view.dispatchUploads(targetCtx, name, files);
|
|
2520
|
+
});
|
|
2402
2521
|
}
|
|
2403
2522
|
__cleanup__() {
|
|
2404
2523
|
this.__listeners.forEach((callbackRef) => this.removeHandleEvent(callbackRef));
|
|
@@ -2424,6 +2543,10 @@ within:
|
|
|
2424
2543
|
isVisible(el) {
|
|
2425
2544
|
return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length > 0);
|
|
2426
2545
|
},
|
|
2546
|
+
isInViewport(el) {
|
|
2547
|
+
const rect = el.getBoundingClientRect();
|
|
2548
|
+
return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
|
|
2549
|
+
},
|
|
2427
2550
|
exec_exec(eventType, phxEvent, view, sourceEl, el, [attr, to]) {
|
|
2428
2551
|
let nodes = to ? dom_default.all(document, to) : [sourceEl];
|
|
2429
2552
|
nodes.forEach((node) => {
|
|
@@ -2682,9 +2805,10 @@ within:
|
|
|
2682
2805
|
this.children = this.parent ? null : {};
|
|
2683
2806
|
this.root.children[this.id] = {};
|
|
2684
2807
|
this.channel = this.liveSocket.channel(`lv:${this.id}`, () => {
|
|
2808
|
+
let url = this.href && this.expandURL(this.href);
|
|
2685
2809
|
return {
|
|
2686
|
-
redirect: this.redirect ?
|
|
2687
|
-
url: this.redirect ? void 0 :
|
|
2810
|
+
redirect: this.redirect ? url : void 0,
|
|
2811
|
+
url: this.redirect ? void 0 : url || void 0,
|
|
2688
2812
|
params: this.connectParams(liveReferer),
|
|
2689
2813
|
session: this.getSession(),
|
|
2690
2814
|
static: this.getStatic(),
|
|
@@ -2882,7 +3006,7 @@ within:
|
|
|
2882
3006
|
this.attachTrueDocEl();
|
|
2883
3007
|
let patch = new DOMPatch(this, this.el, this.id, html, streams, null);
|
|
2884
3008
|
patch.markPrunableContentForRemoval();
|
|
2885
|
-
this.performPatch(patch, false);
|
|
3009
|
+
this.performPatch(patch, false, true);
|
|
2886
3010
|
this.joinNewChildren();
|
|
2887
3011
|
this.execNewMounted();
|
|
2888
3012
|
this.joinPending = false;
|
|
@@ -2921,7 +3045,7 @@ within:
|
|
|
2921
3045
|
newHook.__mounted();
|
|
2922
3046
|
}
|
|
2923
3047
|
}
|
|
2924
|
-
performPatch(patch, pruneCids) {
|
|
3048
|
+
performPatch(patch, pruneCids, isJoinPatch = false) {
|
|
2925
3049
|
let removedEls = [];
|
|
2926
3050
|
let phxChildrenAdded = false;
|
|
2927
3051
|
let updatedHookIds = new Set();
|
|
@@ -2956,7 +3080,7 @@ within:
|
|
|
2956
3080
|
}
|
|
2957
3081
|
});
|
|
2958
3082
|
patch.after("transitionsDiscarded", (els) => this.afterElementsRemoved(els, pruneCids));
|
|
2959
|
-
patch.perform();
|
|
3083
|
+
patch.perform(isJoinPatch);
|
|
2960
3084
|
this.afterElementsRemoved(removedEls, pruneCids);
|
|
2961
3085
|
return phxChildrenAdded;
|
|
2962
3086
|
}
|
|
@@ -3361,7 +3485,7 @@ within:
|
|
|
3361
3485
|
if (isCid(targetCtx)) {
|
|
3362
3486
|
return targetCtx;
|
|
3363
3487
|
}
|
|
3364
|
-
let cidOrSelector = target.getAttribute(this.binding("target"));
|
|
3488
|
+
let cidOrSelector = opts.target || target.getAttribute(this.binding("target"));
|
|
3365
3489
|
if (isCid(cidOrSelector)) {
|
|
3366
3490
|
return parseInt(cidOrSelector);
|
|
3367
3491
|
} else if (targetCtx && (cidOrSelector !== null || opts.target)) {
|
|
@@ -3445,7 +3569,7 @@ within:
|
|
|
3445
3569
|
}
|
|
3446
3570
|
pushInput(inputEl, targetCtx, forceCid, phxEvent, opts, callback) {
|
|
3447
3571
|
let uploads;
|
|
3448
|
-
let cid = isCid(forceCid) ? forceCid : this.targetComponentID(inputEl.form, targetCtx);
|
|
3572
|
+
let cid = isCid(forceCid) ? forceCid : this.targetComponentID(inputEl.form, targetCtx, opts);
|
|
3449
3573
|
let refGenerator = () => this.putRef([inputEl, inputEl.form], "change", opts);
|
|
3450
3574
|
let formData;
|
|
3451
3575
|
let meta = this.extractMeta(inputEl.form);
|
|
@@ -3541,7 +3665,7 @@ within:
|
|
|
3541
3665
|
let cid = this.targetComponentID(formEl, targetCtx);
|
|
3542
3666
|
if (LiveUploader.hasUploadsInProgress(formEl)) {
|
|
3543
3667
|
let [ref, _els] = refGenerator();
|
|
3544
|
-
let push = () => this.pushFormSubmit(formEl,
|
|
3668
|
+
let push = () => this.pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply);
|
|
3545
3669
|
return this.scheduleSubmit(formEl, ref, opts, push);
|
|
3546
3670
|
} else if (LiveUploader.inputsAwaitingPreflight(formEl).length > 0) {
|
|
3547
3671
|
let [ref, els] = refGenerator();
|
|
@@ -3605,8 +3729,9 @@ within:
|
|
|
3605
3729
|
});
|
|
3606
3730
|
});
|
|
3607
3731
|
}
|
|
3608
|
-
dispatchUploads(name, filesOrBlobs) {
|
|
3609
|
-
let
|
|
3732
|
+
dispatchUploads(targetCtx, name, filesOrBlobs) {
|
|
3733
|
+
let targetElement = this.targetCtxElement(targetCtx) || this.el;
|
|
3734
|
+
let inputs = dom_default.findUploadInputs(targetElement).filter((el) => el.name === name);
|
|
3610
3735
|
if (inputs.length === 0) {
|
|
3611
3736
|
logError(`no live file inputs found matching the name "${name}"`);
|
|
3612
3737
|
} else if (inputs.length > 1) {
|
|
@@ -3615,6 +3740,16 @@ within:
|
|
|
3615
3740
|
dom_default.dispatchEvent(inputs[0], PHX_TRACK_UPLOADS, { detail: { files: filesOrBlobs } });
|
|
3616
3741
|
}
|
|
3617
3742
|
}
|
|
3743
|
+
targetCtxElement(targetCtx) {
|
|
3744
|
+
if (isCid(targetCtx)) {
|
|
3745
|
+
let [target] = dom_default.findComponentNodeList(this.el, targetCtx);
|
|
3746
|
+
return target;
|
|
3747
|
+
} else if (targetCtx) {
|
|
3748
|
+
return targetCtx;
|
|
3749
|
+
} else {
|
|
3750
|
+
return null;
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3618
3753
|
pushFormRecovery(form, newCid, callback) {
|
|
3619
3754
|
this.liveSocket.withinOwners(form, (view, targetCtx) => {
|
|
3620
3755
|
let phxChange = this.binding("change");
|
|
@@ -4121,7 +4256,7 @@ within:
|
|
|
4121
4256
|
if (!dead) {
|
|
4122
4257
|
this.bindForms();
|
|
4123
4258
|
}
|
|
4124
|
-
this.bind({ keyup: "keyup", keydown: "keydown" }, (e, type, view, targetEl, phxEvent,
|
|
4259
|
+
this.bind({ keyup: "keyup", keydown: "keydown" }, (e, type, view, targetEl, phxEvent, phxTarget) => {
|
|
4125
4260
|
let matchKey = targetEl.getAttribute(this.binding(PHX_KEY));
|
|
4126
4261
|
let pressedKey = e.key && e.key.toLowerCase();
|
|
4127
4262
|
if (matchKey && matchKey.toLowerCase() !== pressedKey) {
|
|
@@ -4130,13 +4265,13 @@ within:
|
|
|
4130
4265
|
let data = __spreadValues({ key: e.key }, this.eventMeta(type, e, targetEl));
|
|
4131
4266
|
js_default.exec(type, phxEvent, view, targetEl, ["push", { data }]);
|
|
4132
4267
|
});
|
|
4133
|
-
this.bind({ blur: "focusout", focus: "focusin" }, (e, type, view, targetEl, phxEvent,
|
|
4134
|
-
if (!
|
|
4268
|
+
this.bind({ blur: "focusout", focus: "focusin" }, (e, type, view, targetEl, phxEvent, phxTarget) => {
|
|
4269
|
+
if (!phxTarget) {
|
|
4135
4270
|
let data = __spreadValues({ key: e.key }, this.eventMeta(type, e, targetEl));
|
|
4136
4271
|
js_default.exec(type, phxEvent, view, targetEl, ["push", { data }]);
|
|
4137
4272
|
}
|
|
4138
4273
|
});
|
|
4139
|
-
this.bind({ blur: "blur", focus: "focus" }, (e, type, view, targetEl,
|
|
4274
|
+
this.bind({ blur: "blur", focus: "focus" }, (e, type, view, targetEl, phxEvent, phxTarget) => {
|
|
4140
4275
|
if (phxTarget === "window") {
|
|
4141
4276
|
let data = this.eventMeta(type, e, targetEl);
|
|
4142
4277
|
js_default.exec(type, phxEvent, view, targetEl, ["push", { data }]);
|
|
@@ -4217,7 +4352,7 @@ within:
|
|
|
4217
4352
|
}
|
|
4218
4353
|
}
|
|
4219
4354
|
bindClicks() {
|
|
4220
|
-
window.addEventListener("
|
|
4355
|
+
window.addEventListener("mousedown", (e) => this.clickStartedAtTarget = e.target);
|
|
4221
4356
|
this.bindClick("click", "click", false);
|
|
4222
4357
|
this.bindClick("mousedown", "capture-click", true);
|
|
4223
4358
|
}
|
|
@@ -4259,7 +4394,7 @@ within:
|
|
|
4259
4394
|
if (!(el.isSameNode(clickStartedAt) || el.contains(clickStartedAt))) {
|
|
4260
4395
|
this.withinOwners(e.target, (view) => {
|
|
4261
4396
|
let phxEvent = el.getAttribute(phxClickAway);
|
|
4262
|
-
if (js_default.isVisible(el)) {
|
|
4397
|
+
if (js_default.isVisible(el) && js_default.isInViewport(el)) {
|
|
4263
4398
|
js_default.exec("click", phxEvent, view, el, ["push", { data: this.eventMeta("click", e, e.target) }]);
|
|
4264
4399
|
}
|
|
4265
4400
|
});
|
|
@@ -4308,7 +4443,7 @@ within:
|
|
|
4308
4443
|
if (!type || !this.isConnected() || !this.main || dom_default.wantsNewTab(e)) {
|
|
4309
4444
|
return;
|
|
4310
4445
|
}
|
|
4311
|
-
let href = target.href;
|
|
4446
|
+
let href = target.href instanceof SVGAnimatedString ? target.href.baseVal : target.href;
|
|
4312
4447
|
let linkState = target.getAttribute(PHX_LINK_STATE);
|
|
4313
4448
|
e.preventDefault();
|
|
4314
4449
|
e.stopImmediatePropagation();
|