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
|
@@ -40,6 +40,7 @@ var PHX_DROP_TARGET = "drop-target";
|
|
|
40
40
|
var PHX_ACTIVE_ENTRY_REFS = "data-phx-active-refs";
|
|
41
41
|
var PHX_LIVE_FILE_UPDATED = "phx:live-file:updated";
|
|
42
42
|
var PHX_SKIP = "data-phx-skip";
|
|
43
|
+
var PHX_MAGIC_ID = "data-phx-id";
|
|
43
44
|
var PHX_PRUNE = "data-phx-prune";
|
|
44
45
|
var PHX_PAGE_LOADING = "page-loading";
|
|
45
46
|
var PHX_CONNECTED_CLASS = "phx-connected";
|
|
@@ -94,6 +95,7 @@ var DEFAULTS = {
|
|
|
94
95
|
};
|
|
95
96
|
var DYNAMICS = "d";
|
|
96
97
|
var STATIC = "s";
|
|
98
|
+
var ROOT = "r";
|
|
97
99
|
var COMPONENTS = "c";
|
|
98
100
|
var EVENTS = "e";
|
|
99
101
|
var REPLY = "r";
|
|
@@ -116,6 +118,7 @@ var EntryUploader = class {
|
|
|
116
118
|
if (this.errored) {
|
|
117
119
|
return;
|
|
118
120
|
}
|
|
121
|
+
this.uploadChannel.leave();
|
|
119
122
|
this.errored = true;
|
|
120
123
|
clearTimeout(this.chunkTimer);
|
|
121
124
|
this.entry.error(reason);
|
|
@@ -535,12 +538,17 @@ var DOM = {
|
|
|
535
538
|
el.setAttribute("data-phx-hook", "Phoenix.InfiniteScroll");
|
|
536
539
|
}
|
|
537
540
|
},
|
|
538
|
-
maybeHideFeedback(container,
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (input.
|
|
542
|
-
feedbacks.push(input.name
|
|
541
|
+
maybeHideFeedback(container, inputs, phxFeedbackFor) {
|
|
542
|
+
let feedbacks = [];
|
|
543
|
+
inputs.forEach((input) => {
|
|
544
|
+
if (!(this.private(input, PHX_HAS_FOCUSED) || this.private(input, PHX_HAS_SUBMITTED))) {
|
|
545
|
+
feedbacks.push(input.name);
|
|
546
|
+
if (input.name.endsWith("[]")) {
|
|
547
|
+
feedbacks.push(input.name.slice(0, -2));
|
|
548
|
+
}
|
|
543
549
|
}
|
|
550
|
+
});
|
|
551
|
+
if (feedbacks.length > 0) {
|
|
544
552
|
let selector = feedbacks.map((f) => `[${phxFeedbackFor}="${f}"]`).join(", ");
|
|
545
553
|
DOM.all(container, selector, (el) => el.classList.add(PHX_NO_FEEDBACK_CLASS));
|
|
546
554
|
}
|
|
@@ -827,7 +835,8 @@ var UploadEntry = class {
|
|
|
827
835
|
relative_path: this.file.webkitRelativePath,
|
|
828
836
|
size: this.file.size,
|
|
829
837
|
type: this.file.type,
|
|
830
|
-
ref: this.ref
|
|
838
|
+
ref: this.ref,
|
|
839
|
+
meta: typeof this.file.meta === "function" ? this.file.meta() : void 0
|
|
831
840
|
};
|
|
832
841
|
}
|
|
833
842
|
uploader(uploaders) {
|
|
@@ -884,6 +893,9 @@ var LiveUploader = class {
|
|
|
884
893
|
entry.relative_path = file.webkitRelativePath;
|
|
885
894
|
entry.type = file.type;
|
|
886
895
|
entry.size = file.size;
|
|
896
|
+
if (typeof file.meta === "function") {
|
|
897
|
+
entry.meta = file.meta();
|
|
898
|
+
}
|
|
887
899
|
fileData[uploadRef].push(entry);
|
|
888
900
|
});
|
|
889
901
|
return fileData;
|
|
@@ -973,7 +985,7 @@ var ARIA = {
|
|
|
973
985
|
return classes.find((name) => instance instanceof name);
|
|
974
986
|
},
|
|
975
987
|
isFocusable(el, interactiveOnly) {
|
|
976
|
-
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.
|
|
988
|
+
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");
|
|
977
989
|
},
|
|
978
990
|
attemptFocus(el, interactiveOnly) {
|
|
979
991
|
if (this.isFocusable(el, interactiveOnly)) {
|
|
@@ -1549,7 +1561,7 @@ function morphdomFactory(morphAttrs2) {
|
|
|
1549
1561
|
}
|
|
1550
1562
|
}
|
|
1551
1563
|
function morphChildren(fromEl, toEl) {
|
|
1552
|
-
var skipFrom = skipFromChildren(fromEl);
|
|
1564
|
+
var skipFrom = skipFromChildren(fromEl, toEl);
|
|
1553
1565
|
var curToNodeChild = toEl.firstChild;
|
|
1554
1566
|
var curFromNodeChild = fromEl.firstChild;
|
|
1555
1567
|
var curToNodeKey;
|
|
@@ -1752,7 +1764,7 @@ var DOMPatch = class {
|
|
|
1752
1764
|
el.setAttribute(PHX_PRUNE, "");
|
|
1753
1765
|
});
|
|
1754
1766
|
}
|
|
1755
|
-
perform() {
|
|
1767
|
+
perform(isJoinPatch) {
|
|
1756
1768
|
let { view, liveSocket, container, html } = this;
|
|
1757
1769
|
let targetContainer = this.isCIDPatch() ? this.targetCIDContainer(html) : container;
|
|
1758
1770
|
if (this.isCIDPatch() && !targetContainer) {
|
|
@@ -1771,19 +1783,18 @@ var DOMPatch = class {
|
|
|
1771
1783
|
let updates = [];
|
|
1772
1784
|
let appendPrependUpdates = [];
|
|
1773
1785
|
let externalFormTriggered = null;
|
|
1774
|
-
let diffHTML = liveSocket.time("premorph container prep", () => {
|
|
1775
|
-
return this.buildDiffHTML(container, html, phxUpdate, targetContainer);
|
|
1776
|
-
});
|
|
1777
1786
|
this.trackBefore("added", container);
|
|
1778
1787
|
this.trackBefore("updated", container, container);
|
|
1779
1788
|
liveSocket.time("morphdom", () => {
|
|
1780
1789
|
this.streams.forEach(([ref, inserts, deleteIds, reset]) => {
|
|
1781
1790
|
Object.entries(inserts).forEach(([key, [streamAt, limit]]) => {
|
|
1782
|
-
this.streamInserts[key] = { ref, streamAt, limit };
|
|
1791
|
+
this.streamInserts[key] = { ref, streamAt, limit, resetKept: false };
|
|
1783
1792
|
});
|
|
1784
1793
|
if (reset !== void 0) {
|
|
1785
1794
|
dom_default.all(container, `[${PHX_STREAM_REF}="${ref}"]`, (child) => {
|
|
1786
|
-
if (
|
|
1795
|
+
if (inserts[child.id]) {
|
|
1796
|
+
this.streamInserts[child.id].resetKept = true;
|
|
1797
|
+
} else {
|
|
1787
1798
|
this.removeStreamChildElement(child);
|
|
1788
1799
|
}
|
|
1789
1800
|
});
|
|
@@ -1795,10 +1806,16 @@ var DOMPatch = class {
|
|
|
1795
1806
|
}
|
|
1796
1807
|
});
|
|
1797
1808
|
});
|
|
1798
|
-
morphdom_esm_default(targetContainer,
|
|
1809
|
+
morphdom_esm_default(targetContainer, html, {
|
|
1799
1810
|
childrenOnly: targetContainer.getAttribute(PHX_COMPONENT) === null,
|
|
1800
1811
|
getNodeKey: (node) => {
|
|
1801
|
-
|
|
1812
|
+
if (dom_default.isPhxDestroyed(node)) {
|
|
1813
|
+
return null;
|
|
1814
|
+
}
|
|
1815
|
+
if (isJoinPatch) {
|
|
1816
|
+
return node.id;
|
|
1817
|
+
}
|
|
1818
|
+
return node.id || node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
|
|
1802
1819
|
},
|
|
1803
1820
|
skipFromChildren: (from) => {
|
|
1804
1821
|
return from.getAttribute(phxUpdate) === PHX_STREAM;
|
|
@@ -1855,6 +1872,25 @@ var DOMPatch = class {
|
|
|
1855
1872
|
}
|
|
1856
1873
|
added.push(el);
|
|
1857
1874
|
},
|
|
1875
|
+
onBeforeElChildrenUpdated: (fromEl, toEl) => {
|
|
1876
|
+
if (fromEl.getAttribute(phxUpdate) === PHX_STREAM) {
|
|
1877
|
+
let toIds = Array.from(toEl.children).map((child) => child.id);
|
|
1878
|
+
Array.from(fromEl.children).filter((child) => {
|
|
1879
|
+
let { resetKept } = this.getStreamInsert(child);
|
|
1880
|
+
return resetKept;
|
|
1881
|
+
}).sort((a, b) => {
|
|
1882
|
+
let aIdx = toIds.indexOf(a.id);
|
|
1883
|
+
let bIdx = toIds.indexOf(b.id);
|
|
1884
|
+
if (aIdx === bIdx) {
|
|
1885
|
+
return 0;
|
|
1886
|
+
} else if (aIdx < bIdx) {
|
|
1887
|
+
return -1;
|
|
1888
|
+
} else {
|
|
1889
|
+
return 1;
|
|
1890
|
+
}
|
|
1891
|
+
}).forEach((child) => fromEl.appendChild(child));
|
|
1892
|
+
}
|
|
1893
|
+
},
|
|
1858
1894
|
onNodeDiscarded: (el) => this.onNodeDiscarded(el),
|
|
1859
1895
|
onBeforeNodeDiscarded: (el) => {
|
|
1860
1896
|
if (el.getAttribute && el.getAttribute(PHX_PRUNE) !== null) {
|
|
@@ -1948,9 +1984,7 @@ var DOMPatch = class {
|
|
|
1948
1984
|
appendPrependUpdates.forEach((update) => update.perform());
|
|
1949
1985
|
});
|
|
1950
1986
|
}
|
|
1951
|
-
|
|
1952
|
-
dom_default.maybeHideFeedback(targetContainer, input, phxFeedbackFor);
|
|
1953
|
-
});
|
|
1987
|
+
dom_default.maybeHideFeedback(targetContainer, trackedInputs, phxFeedbackFor);
|
|
1954
1988
|
liveSocket.silenceEvents(() => dom_default.restoreFocus(focused, selectionStart, selectionEnd));
|
|
1955
1989
|
dom_default.dispatchEvent(document, "phx:update");
|
|
1956
1990
|
added.forEach((el) => this.trackAfter("added", el));
|
|
@@ -2029,7 +2063,7 @@ var DOMPatch = class {
|
|
|
2029
2063
|
return this.cidPatch;
|
|
2030
2064
|
}
|
|
2031
2065
|
skipCIDSibling(el) {
|
|
2032
|
-
return el.nodeType === Node.ELEMENT_NODE && el.
|
|
2066
|
+
return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
|
|
2033
2067
|
}
|
|
2034
2068
|
targetCIDContainer(html) {
|
|
2035
2069
|
if (!this.isCIDPatch()) {
|
|
@@ -2042,35 +2076,125 @@ var DOMPatch = class {
|
|
|
2042
2076
|
return first && first.parentNode;
|
|
2043
2077
|
}
|
|
2044
2078
|
}
|
|
2045
|
-
buildDiffHTML(container, html, phxUpdate, targetContainer) {
|
|
2046
|
-
let isCIDPatch = this.isCIDPatch();
|
|
2047
|
-
let isCIDWithSingleRoot = isCIDPatch && targetContainer.getAttribute(PHX_COMPONENT) === this.targetCID.toString();
|
|
2048
|
-
if (!isCIDPatch || isCIDWithSingleRoot) {
|
|
2049
|
-
return html;
|
|
2050
|
-
} else {
|
|
2051
|
-
let diffContainer = null;
|
|
2052
|
-
let template = document.createElement("template");
|
|
2053
|
-
diffContainer = dom_default.cloneNode(targetContainer);
|
|
2054
|
-
let [firstComponent, ...rest] = dom_default.findComponentNodeList(diffContainer, this.targetCID);
|
|
2055
|
-
template.innerHTML = html;
|
|
2056
|
-
rest.forEach((el) => el.remove());
|
|
2057
|
-
Array.from(diffContainer.childNodes).forEach((child) => {
|
|
2058
|
-
if (child.id && child.nodeType === Node.ELEMENT_NODE && child.getAttribute(PHX_COMPONENT) !== this.targetCID.toString()) {
|
|
2059
|
-
child.setAttribute(PHX_SKIP, "");
|
|
2060
|
-
child.innerHTML = "";
|
|
2061
|
-
}
|
|
2062
|
-
});
|
|
2063
|
-
Array.from(template.content.childNodes).forEach((el) => diffContainer.insertBefore(el, firstComponent));
|
|
2064
|
-
firstComponent.remove();
|
|
2065
|
-
return diffContainer.outerHTML;
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
2079
|
indexOf(parent, child) {
|
|
2069
2080
|
return Array.from(parent.children).indexOf(child);
|
|
2070
2081
|
}
|
|
2071
2082
|
};
|
|
2072
2083
|
|
|
2073
2084
|
// js/phoenix_live_view/rendered.js
|
|
2085
|
+
var VOID_TAGS = new Set([
|
|
2086
|
+
"area",
|
|
2087
|
+
"base",
|
|
2088
|
+
"br",
|
|
2089
|
+
"col",
|
|
2090
|
+
"command",
|
|
2091
|
+
"embed",
|
|
2092
|
+
"hr",
|
|
2093
|
+
"img",
|
|
2094
|
+
"input",
|
|
2095
|
+
"keygen",
|
|
2096
|
+
"link",
|
|
2097
|
+
"meta",
|
|
2098
|
+
"param",
|
|
2099
|
+
"source",
|
|
2100
|
+
"track",
|
|
2101
|
+
"wbr"
|
|
2102
|
+
]);
|
|
2103
|
+
var endingTagNameChars = new Set([">", "/", " ", "\n", " ", "\r"]);
|
|
2104
|
+
var quoteChars = new Set(["'", '"']);
|
|
2105
|
+
var modifyRoot = (html, attrs, clearInnerHTML) => {
|
|
2106
|
+
let i = 0;
|
|
2107
|
+
let insideComment = false;
|
|
2108
|
+
let beforeTag, afterTag, tag, tagNameEndsAt, id, newHTML;
|
|
2109
|
+
while (i < html.length) {
|
|
2110
|
+
let char = html.charAt(i);
|
|
2111
|
+
if (insideComment) {
|
|
2112
|
+
if (char === "-" && html.slice(i, i + 3) === "-->") {
|
|
2113
|
+
insideComment = false;
|
|
2114
|
+
i += 3;
|
|
2115
|
+
} else {
|
|
2116
|
+
i++;
|
|
2117
|
+
}
|
|
2118
|
+
} else if (char === "<" && html.slice(i, i + 4) === "<!--") {
|
|
2119
|
+
insideComment = true;
|
|
2120
|
+
i += 4;
|
|
2121
|
+
} else if (char === "<") {
|
|
2122
|
+
beforeTag = html.slice(0, i);
|
|
2123
|
+
let iAtOpen = i;
|
|
2124
|
+
i++;
|
|
2125
|
+
for (i; i < html.length; i++) {
|
|
2126
|
+
if (endingTagNameChars.has(html.charAt(i))) {
|
|
2127
|
+
break;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
tagNameEndsAt = i;
|
|
2131
|
+
tag = html.slice(iAtOpen + 1, tagNameEndsAt);
|
|
2132
|
+
for (i; i < html.length; i++) {
|
|
2133
|
+
if (html.charAt(i) === ">") {
|
|
2134
|
+
break;
|
|
2135
|
+
}
|
|
2136
|
+
if (html.charAt(i) === "=") {
|
|
2137
|
+
let isId = html.slice(i - 3, i) === " id";
|
|
2138
|
+
i++;
|
|
2139
|
+
let char2 = html.charAt(i);
|
|
2140
|
+
if (quoteChars.has(char2)) {
|
|
2141
|
+
let attrStartsAt = i;
|
|
2142
|
+
i++;
|
|
2143
|
+
for (i; i < html.length; i++) {
|
|
2144
|
+
if (html.charAt(i) === char2) {
|
|
2145
|
+
break;
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
if (isId) {
|
|
2149
|
+
id = html.slice(attrStartsAt + 1, i);
|
|
2150
|
+
break;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
break;
|
|
2156
|
+
} else {
|
|
2157
|
+
i++;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
if (!tag) {
|
|
2161
|
+
throw new Error(`malformed html ${html}`);
|
|
2162
|
+
}
|
|
2163
|
+
let closeAt = html.length - 1;
|
|
2164
|
+
insideComment = false;
|
|
2165
|
+
while (closeAt >= beforeTag.length + tag.length) {
|
|
2166
|
+
let char = html.charAt(closeAt);
|
|
2167
|
+
if (insideComment) {
|
|
2168
|
+
if (char === "-" && html.slice(closeAt - 3, closeAt) === "<!-") {
|
|
2169
|
+
insideComment = false;
|
|
2170
|
+
closeAt -= 4;
|
|
2171
|
+
} else {
|
|
2172
|
+
closeAt -= 1;
|
|
2173
|
+
}
|
|
2174
|
+
} else if (char === ">" && html.slice(closeAt - 2, closeAt) === "--") {
|
|
2175
|
+
insideComment = true;
|
|
2176
|
+
closeAt -= 3;
|
|
2177
|
+
} else if (char === ">") {
|
|
2178
|
+
break;
|
|
2179
|
+
} else {
|
|
2180
|
+
closeAt -= 1;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
afterTag = html.slice(closeAt + 1, html.length);
|
|
2184
|
+
let attrsStr = Object.keys(attrs).map((attr) => attrs[attr] === true ? attr : `${attr}="${attrs[attr]}"`).join(" ");
|
|
2185
|
+
if (clearInnerHTML) {
|
|
2186
|
+
let idAttrStr = id ? ` id="${id}"` : "";
|
|
2187
|
+
if (VOID_TAGS.has(tag)) {
|
|
2188
|
+
newHTML = `<${tag}${idAttrStr}${attrsStr === "" ? "" : " "}${attrsStr}/>`;
|
|
2189
|
+
} else {
|
|
2190
|
+
newHTML = `<${tag}${idAttrStr}${attrsStr === "" ? "" : " "}${attrsStr}></${tag}>`;
|
|
2191
|
+
}
|
|
2192
|
+
} else {
|
|
2193
|
+
let rest = html.slice(tagNameEndsAt, closeAt + 1);
|
|
2194
|
+
newHTML = `<${tag}${attrsStr === "" ? "" : " "}${attrsStr}${rest}`;
|
|
2195
|
+
}
|
|
2196
|
+
return [newHTML, beforeTag, afterTag];
|
|
2197
|
+
};
|
|
2074
2198
|
var Rendered = class {
|
|
2075
2199
|
static extract(diff) {
|
|
2076
2200
|
let { [REPLY]: reply, [EVENTS]: events, [TITLE]: title } = diff;
|
|
@@ -2082,19 +2206,20 @@ var Rendered = class {
|
|
|
2082
2206
|
constructor(viewId, rendered) {
|
|
2083
2207
|
this.viewId = viewId;
|
|
2084
2208
|
this.rendered = {};
|
|
2209
|
+
this.magicId = 0;
|
|
2085
2210
|
this.mergeDiff(rendered);
|
|
2086
2211
|
}
|
|
2087
2212
|
parentViewId() {
|
|
2088
2213
|
return this.viewId;
|
|
2089
2214
|
}
|
|
2090
2215
|
toString(onlyCids) {
|
|
2091
|
-
let [str, streams] = this.recursiveToString(this.rendered, this.rendered[COMPONENTS], onlyCids);
|
|
2216
|
+
let [str, streams] = this.recursiveToString(this.rendered, this.rendered[COMPONENTS], onlyCids, true, {});
|
|
2092
2217
|
return [str, streams];
|
|
2093
2218
|
}
|
|
2094
|
-
recursiveToString(rendered, components = rendered[COMPONENTS], onlyCids) {
|
|
2219
|
+
recursiveToString(rendered, components = rendered[COMPONENTS], onlyCids, changeTracking, rootAttrs) {
|
|
2095
2220
|
onlyCids = onlyCids ? new Set(onlyCids) : null;
|
|
2096
2221
|
let output = { buffer: "", components, onlyCids, streams: new Set() };
|
|
2097
|
-
this.toOutputBuffer(rendered, null, output);
|
|
2222
|
+
this.toOutputBuffer(rendered, null, output, changeTracking, rootAttrs);
|
|
2098
2223
|
return [output.buffer, output.streams];
|
|
2099
2224
|
}
|
|
2100
2225
|
componentCIDs(diff) {
|
|
@@ -2139,10 +2264,10 @@ var Rendered = class {
|
|
|
2139
2264
|
tdiff = oldc[-scid];
|
|
2140
2265
|
}
|
|
2141
2266
|
stat = tdiff[STATIC];
|
|
2142
|
-
ndiff = this.cloneMerge(tdiff, cdiff);
|
|
2267
|
+
ndiff = this.cloneMerge(tdiff, cdiff, true);
|
|
2143
2268
|
ndiff[STATIC] = stat;
|
|
2144
2269
|
} else {
|
|
2145
|
-
ndiff = cdiff[STATIC] !== void 0 ? cdiff : this.cloneMerge(oldc[cid]
|
|
2270
|
+
ndiff = cdiff[STATIC] !== void 0 || oldc[cid] === void 0 ? cdiff : this.cloneMerge(oldc[cid], cdiff, false);
|
|
2146
2271
|
}
|
|
2147
2272
|
cache[cid] = ndiff;
|
|
2148
2273
|
return ndiff;
|
|
@@ -2167,21 +2292,31 @@ var Rendered = class {
|
|
|
2167
2292
|
target[key] = val;
|
|
2168
2293
|
}
|
|
2169
2294
|
}
|
|
2295
|
+
if (target[ROOT]) {
|
|
2296
|
+
target.newRender = true;
|
|
2297
|
+
}
|
|
2170
2298
|
}
|
|
2171
|
-
cloneMerge(target, source) {
|
|
2299
|
+
cloneMerge(target, source, pruneMagicId) {
|
|
2172
2300
|
let merged = { ...target, ...source };
|
|
2173
2301
|
for (let key in merged) {
|
|
2174
2302
|
let val = source[key];
|
|
2175
2303
|
let targetVal = target[key];
|
|
2176
2304
|
if (isObject(val) && val[STATIC] === void 0 && isObject(targetVal)) {
|
|
2177
|
-
merged[key] = this.cloneMerge(targetVal, val);
|
|
2305
|
+
merged[key] = this.cloneMerge(targetVal, val, pruneMagicId);
|
|
2178
2306
|
}
|
|
2179
2307
|
}
|
|
2308
|
+
if (pruneMagicId) {
|
|
2309
|
+
delete merged.magicId;
|
|
2310
|
+
delete merged.newRender;
|
|
2311
|
+
} else if (target[ROOT]) {
|
|
2312
|
+
merged.newRender = true;
|
|
2313
|
+
}
|
|
2180
2314
|
return merged;
|
|
2181
2315
|
}
|
|
2182
2316
|
componentToString(cid) {
|
|
2183
|
-
let [str, streams] = this.recursiveCIDToString(this.rendered[COMPONENTS], cid, null
|
|
2184
|
-
|
|
2317
|
+
let [str, streams] = this.recursiveCIDToString(this.rendered[COMPONENTS], cid, null);
|
|
2318
|
+
let [strippedHTML, _before, _after] = modifyRoot(str, {});
|
|
2319
|
+
return [strippedHTML, streams];
|
|
2185
2320
|
}
|
|
2186
2321
|
pruneCIDs(cids) {
|
|
2187
2322
|
cids.forEach((cid) => delete this.rendered[COMPONENTS][cid]);
|
|
@@ -2199,17 +2334,46 @@ var Rendered = class {
|
|
|
2199
2334
|
return part;
|
|
2200
2335
|
}
|
|
2201
2336
|
}
|
|
2202
|
-
|
|
2337
|
+
nextMagicID() {
|
|
2338
|
+
this.magicId++;
|
|
2339
|
+
return `${this.parentViewId()}-${this.magicId}`;
|
|
2340
|
+
}
|
|
2341
|
+
toOutputBuffer(rendered, templates, output, changeTracking, rootAttrs = {}) {
|
|
2203
2342
|
if (rendered[DYNAMICS]) {
|
|
2204
2343
|
return this.comprehensionToBuffer(rendered, templates, output);
|
|
2205
2344
|
}
|
|
2206
2345
|
let { [STATIC]: statics } = rendered;
|
|
2207
2346
|
statics = this.templateStatic(statics, templates);
|
|
2347
|
+
let isRoot = rendered[ROOT];
|
|
2348
|
+
let prevBuffer = output.buffer;
|
|
2349
|
+
if (isRoot) {
|
|
2350
|
+
output.buffer = "";
|
|
2351
|
+
}
|
|
2352
|
+
if (changeTracking && isRoot && !rendered.magicId) {
|
|
2353
|
+
rendered.newRender = true;
|
|
2354
|
+
rendered.magicId = this.nextMagicID();
|
|
2355
|
+
}
|
|
2208
2356
|
output.buffer += statics[0];
|
|
2209
2357
|
for (let i = 1; i < statics.length; i++) {
|
|
2210
|
-
this.dynamicToBuffer(rendered[i - 1], templates, output);
|
|
2358
|
+
this.dynamicToBuffer(rendered[i - 1], templates, output, changeTracking);
|
|
2211
2359
|
output.buffer += statics[i];
|
|
2212
2360
|
}
|
|
2361
|
+
if (isRoot) {
|
|
2362
|
+
let skip = false;
|
|
2363
|
+
let attrs;
|
|
2364
|
+
if (changeTracking || Object.keys(rootAttrs).length > 0) {
|
|
2365
|
+
skip = !rendered.newRender;
|
|
2366
|
+
attrs = { [PHX_MAGIC_ID]: rendered.magicId, ...rootAttrs };
|
|
2367
|
+
} else {
|
|
2368
|
+
attrs = rootAttrs;
|
|
2369
|
+
}
|
|
2370
|
+
if (skip) {
|
|
2371
|
+
attrs[PHX_SKIP] = true;
|
|
2372
|
+
}
|
|
2373
|
+
let [newRoot, commentBefore, commentAfter] = modifyRoot(output.buffer, attrs, skip);
|
|
2374
|
+
rendered.newRender = false;
|
|
2375
|
+
output.buffer = prevBuffer + commentBefore + newRoot + commentAfter;
|
|
2376
|
+
}
|
|
2213
2377
|
}
|
|
2214
2378
|
comprehensionToBuffer(rendered, templates, output) {
|
|
2215
2379
|
let { [DYNAMICS]: dynamics, [STATIC]: statics, [STREAM]: stream } = rendered;
|
|
@@ -2220,7 +2384,8 @@ var Rendered = class {
|
|
|
2220
2384
|
let dynamic = dynamics[d];
|
|
2221
2385
|
output.buffer += statics[0];
|
|
2222
2386
|
for (let i = 1; i < statics.length; i++) {
|
|
2223
|
-
|
|
2387
|
+
let changeTracking = false;
|
|
2388
|
+
this.dynamicToBuffer(dynamic[i - 1], compTemplates, output, changeTracking);
|
|
2224
2389
|
output.buffer += statics[i];
|
|
2225
2390
|
}
|
|
2226
2391
|
}
|
|
@@ -2230,74 +2395,26 @@ var Rendered = class {
|
|
|
2230
2395
|
output.streams.add(stream);
|
|
2231
2396
|
}
|
|
2232
2397
|
}
|
|
2233
|
-
dynamicToBuffer(rendered, templates, output) {
|
|
2398
|
+
dynamicToBuffer(rendered, templates, output, changeTracking) {
|
|
2234
2399
|
if (typeof rendered === "number") {
|
|
2235
2400
|
let [str, streams] = this.recursiveCIDToString(output.components, rendered, output.onlyCids);
|
|
2236
2401
|
output.buffer += str;
|
|
2237
2402
|
output.streams = new Set([...output.streams, ...streams]);
|
|
2238
2403
|
} else if (isObject(rendered)) {
|
|
2239
|
-
this.toOutputBuffer(rendered, templates, output);
|
|
2404
|
+
this.toOutputBuffer(rendered, templates, output, changeTracking, {});
|
|
2240
2405
|
} else {
|
|
2241
2406
|
output.buffer += rendered;
|
|
2242
2407
|
}
|
|
2243
2408
|
}
|
|
2244
|
-
recursiveCIDToString(components, cid, onlyCids
|
|
2409
|
+
recursiveCIDToString(components, cid, onlyCids) {
|
|
2245
2410
|
let component = components[cid] || logError(`no component for CID ${cid}`, components);
|
|
2246
|
-
let
|
|
2247
|
-
let [html, streams] = this.recursiveToString(component, components, onlyCids);
|
|
2248
|
-
template.innerHTML = html;
|
|
2249
|
-
let container = template.content;
|
|
2411
|
+
let attrs = { [PHX_COMPONENT]: cid };
|
|
2250
2412
|
let skip = onlyCids && !onlyCids.has(cid);
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
child.setAttribute(PHX_COMPONENT, cid);
|
|
2257
|
-
if (!child.id) {
|
|
2258
|
-
child.id = `${this.parentViewId()}-${cid}-${i}`;
|
|
2259
|
-
}
|
|
2260
|
-
if (skip) {
|
|
2261
|
-
child.setAttribute(PHX_SKIP, "");
|
|
2262
|
-
child.innerHTML = "";
|
|
2263
|
-
}
|
|
2264
|
-
return [true, hasComponents];
|
|
2265
|
-
} else if (child.nodeType === Node.COMMENT_NODE) {
|
|
2266
|
-
if (!allowRootComments) {
|
|
2267
|
-
child.remove();
|
|
2268
|
-
}
|
|
2269
|
-
return [hasNodes, hasComponents];
|
|
2270
|
-
} else {
|
|
2271
|
-
if (child.nodeValue.trim() !== "") {
|
|
2272
|
-
logError(`only HTML element tags are allowed at the root of components.
|
|
2273
|
-
|
|
2274
|
-
got: "${child.nodeValue.trim()}"
|
|
2275
|
-
|
|
2276
|
-
within:
|
|
2277
|
-
`, template.innerHTML.trim());
|
|
2278
|
-
child.replaceWith(this.createSpan(child.nodeValue, cid));
|
|
2279
|
-
return [true, hasComponents];
|
|
2280
|
-
} else {
|
|
2281
|
-
child.remove();
|
|
2282
|
-
return [hasNodes, hasComponents];
|
|
2283
|
-
}
|
|
2284
|
-
}
|
|
2285
|
-
}, [false, false]);
|
|
2286
|
-
if (!hasChildNodes && !hasChildComponents) {
|
|
2287
|
-
logError("expected at least one HTML element tag inside a component, but the component is empty:\n", template.innerHTML.trim());
|
|
2288
|
-
return [this.createSpan("", cid).outerHTML, streams];
|
|
2289
|
-
} else if (!hasChildNodes && hasChildComponents) {
|
|
2290
|
-
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());
|
|
2291
|
-
return [template.innerHTML, streams];
|
|
2292
|
-
} else {
|
|
2293
|
-
return [template.innerHTML, streams];
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2296
|
-
createSpan(text, cid) {
|
|
2297
|
-
let span = document.createElement("span");
|
|
2298
|
-
span.innerText = text;
|
|
2299
|
-
span.setAttribute(PHX_COMPONENT, cid);
|
|
2300
|
-
return span;
|
|
2413
|
+
component.newRender = !skip;
|
|
2414
|
+
component.magicId = `${this.parentViewId()}-c-${cid}`;
|
|
2415
|
+
let changeTracking = true;
|
|
2416
|
+
let [html, streams] = this.recursiveToString(component, components, onlyCids, changeTracking, attrs);
|
|
2417
|
+
return [html, streams];
|
|
2301
2418
|
}
|
|
2302
2419
|
};
|
|
2303
2420
|
|
|
@@ -2366,10 +2483,12 @@ var ViewHook = class {
|
|
|
2366
2483
|
this.__listeners.delete(callbackRef);
|
|
2367
2484
|
}
|
|
2368
2485
|
upload(name, files) {
|
|
2369
|
-
return this.__view.dispatchUploads(name, files);
|
|
2486
|
+
return this.__view.dispatchUploads(null, name, files);
|
|
2370
2487
|
}
|
|
2371
2488
|
uploadTo(phxTarget, name, files) {
|
|
2372
|
-
return this.__view.withinTargets(phxTarget, (view) =>
|
|
2489
|
+
return this.__view.withinTargets(phxTarget, (view, targetCtx) => {
|
|
2490
|
+
view.dispatchUploads(targetCtx, name, files);
|
|
2491
|
+
});
|
|
2373
2492
|
}
|
|
2374
2493
|
__cleanup__() {
|
|
2375
2494
|
this.__listeners.forEach((callbackRef) => this.removeHandleEvent(callbackRef));
|
|
@@ -2395,6 +2514,10 @@ var JS = {
|
|
|
2395
2514
|
isVisible(el) {
|
|
2396
2515
|
return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length > 0);
|
|
2397
2516
|
},
|
|
2517
|
+
isInViewport(el) {
|
|
2518
|
+
const rect = el.getBoundingClientRect();
|
|
2519
|
+
return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
|
|
2520
|
+
},
|
|
2398
2521
|
exec_exec(eventType, phxEvent, view, sourceEl, el, [attr, to]) {
|
|
2399
2522
|
let nodes = to ? dom_default.all(document, to) : [sourceEl];
|
|
2400
2523
|
nodes.forEach((node) => {
|
|
@@ -2653,9 +2776,10 @@ var View = class {
|
|
|
2653
2776
|
this.children = this.parent ? null : {};
|
|
2654
2777
|
this.root.children[this.id] = {};
|
|
2655
2778
|
this.channel = this.liveSocket.channel(`lv:${this.id}`, () => {
|
|
2779
|
+
let url = this.href && this.expandURL(this.href);
|
|
2656
2780
|
return {
|
|
2657
|
-
redirect: this.redirect ?
|
|
2658
|
-
url: this.redirect ? void 0 :
|
|
2781
|
+
redirect: this.redirect ? url : void 0,
|
|
2782
|
+
url: this.redirect ? void 0 : url || void 0,
|
|
2659
2783
|
params: this.connectParams(liveReferer),
|
|
2660
2784
|
session: this.getSession(),
|
|
2661
2785
|
static: this.getStatic(),
|
|
@@ -2853,7 +2977,7 @@ var View = class {
|
|
|
2853
2977
|
this.attachTrueDocEl();
|
|
2854
2978
|
let patch = new DOMPatch(this, this.el, this.id, html, streams, null);
|
|
2855
2979
|
patch.markPrunableContentForRemoval();
|
|
2856
|
-
this.performPatch(patch, false);
|
|
2980
|
+
this.performPatch(patch, false, true);
|
|
2857
2981
|
this.joinNewChildren();
|
|
2858
2982
|
this.execNewMounted();
|
|
2859
2983
|
this.joinPending = false;
|
|
@@ -2892,7 +3016,7 @@ var View = class {
|
|
|
2892
3016
|
newHook.__mounted();
|
|
2893
3017
|
}
|
|
2894
3018
|
}
|
|
2895
|
-
performPatch(patch, pruneCids) {
|
|
3019
|
+
performPatch(patch, pruneCids, isJoinPatch = false) {
|
|
2896
3020
|
let removedEls = [];
|
|
2897
3021
|
let phxChildrenAdded = false;
|
|
2898
3022
|
let updatedHookIds = new Set();
|
|
@@ -2927,7 +3051,7 @@ var View = class {
|
|
|
2927
3051
|
}
|
|
2928
3052
|
});
|
|
2929
3053
|
patch.after("transitionsDiscarded", (els) => this.afterElementsRemoved(els, pruneCids));
|
|
2930
|
-
patch.perform();
|
|
3054
|
+
patch.perform(isJoinPatch);
|
|
2931
3055
|
this.afterElementsRemoved(removedEls, pruneCids);
|
|
2932
3056
|
return phxChildrenAdded;
|
|
2933
3057
|
}
|
|
@@ -3332,7 +3456,7 @@ var View = class {
|
|
|
3332
3456
|
if (isCid(targetCtx)) {
|
|
3333
3457
|
return targetCtx;
|
|
3334
3458
|
}
|
|
3335
|
-
let cidOrSelector = target.getAttribute(this.binding("target"));
|
|
3459
|
+
let cidOrSelector = opts.target || target.getAttribute(this.binding("target"));
|
|
3336
3460
|
if (isCid(cidOrSelector)) {
|
|
3337
3461
|
return parseInt(cidOrSelector);
|
|
3338
3462
|
} else if (targetCtx && (cidOrSelector !== null || opts.target)) {
|
|
@@ -3416,7 +3540,7 @@ var View = class {
|
|
|
3416
3540
|
}
|
|
3417
3541
|
pushInput(inputEl, targetCtx, forceCid, phxEvent, opts, callback) {
|
|
3418
3542
|
let uploads;
|
|
3419
|
-
let cid = isCid(forceCid) ? forceCid : this.targetComponentID(inputEl.form, targetCtx);
|
|
3543
|
+
let cid = isCid(forceCid) ? forceCid : this.targetComponentID(inputEl.form, targetCtx, opts);
|
|
3420
3544
|
let refGenerator = () => this.putRef([inputEl, inputEl.form], "change", opts);
|
|
3421
3545
|
let formData;
|
|
3422
3546
|
let meta = this.extractMeta(inputEl.form);
|
|
@@ -3512,7 +3636,7 @@ var View = class {
|
|
|
3512
3636
|
let cid = this.targetComponentID(formEl, targetCtx);
|
|
3513
3637
|
if (LiveUploader.hasUploadsInProgress(formEl)) {
|
|
3514
3638
|
let [ref, _els] = refGenerator();
|
|
3515
|
-
let push = () => this.pushFormSubmit(formEl,
|
|
3639
|
+
let push = () => this.pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply);
|
|
3516
3640
|
return this.scheduleSubmit(formEl, ref, opts, push);
|
|
3517
3641
|
} else if (LiveUploader.inputsAwaitingPreflight(formEl).length > 0) {
|
|
3518
3642
|
let [ref, els] = refGenerator();
|
|
@@ -3576,8 +3700,9 @@ var View = class {
|
|
|
3576
3700
|
});
|
|
3577
3701
|
});
|
|
3578
3702
|
}
|
|
3579
|
-
dispatchUploads(name, filesOrBlobs) {
|
|
3580
|
-
let
|
|
3703
|
+
dispatchUploads(targetCtx, name, filesOrBlobs) {
|
|
3704
|
+
let targetElement = this.targetCtxElement(targetCtx) || this.el;
|
|
3705
|
+
let inputs = dom_default.findUploadInputs(targetElement).filter((el) => el.name === name);
|
|
3581
3706
|
if (inputs.length === 0) {
|
|
3582
3707
|
logError(`no live file inputs found matching the name "${name}"`);
|
|
3583
3708
|
} else if (inputs.length > 1) {
|
|
@@ -3586,6 +3711,16 @@ var View = class {
|
|
|
3586
3711
|
dom_default.dispatchEvent(inputs[0], PHX_TRACK_UPLOADS, { detail: { files: filesOrBlobs } });
|
|
3587
3712
|
}
|
|
3588
3713
|
}
|
|
3714
|
+
targetCtxElement(targetCtx) {
|
|
3715
|
+
if (isCid(targetCtx)) {
|
|
3716
|
+
let [target] = dom_default.findComponentNodeList(this.el, targetCtx);
|
|
3717
|
+
return target;
|
|
3718
|
+
} else if (targetCtx) {
|
|
3719
|
+
return targetCtx;
|
|
3720
|
+
} else {
|
|
3721
|
+
return null;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3589
3724
|
pushFormRecovery(form, newCid, callback) {
|
|
3590
3725
|
this.liveSocket.withinOwners(form, (view, targetCtx) => {
|
|
3591
3726
|
let phxChange = this.binding("change");
|
|
@@ -4092,7 +4227,7 @@ var LiveSocket = class {
|
|
|
4092
4227
|
if (!dead) {
|
|
4093
4228
|
this.bindForms();
|
|
4094
4229
|
}
|
|
4095
|
-
this.bind({ keyup: "keyup", keydown: "keydown" }, (e, type, view, targetEl, phxEvent,
|
|
4230
|
+
this.bind({ keyup: "keyup", keydown: "keydown" }, (e, type, view, targetEl, phxEvent, phxTarget) => {
|
|
4096
4231
|
let matchKey = targetEl.getAttribute(this.binding(PHX_KEY));
|
|
4097
4232
|
let pressedKey = e.key && e.key.toLowerCase();
|
|
4098
4233
|
if (matchKey && matchKey.toLowerCase() !== pressedKey) {
|
|
@@ -4101,13 +4236,13 @@ var LiveSocket = class {
|
|
|
4101
4236
|
let data = { key: e.key, ...this.eventMeta(type, e, targetEl) };
|
|
4102
4237
|
js_default.exec(type, phxEvent, view, targetEl, ["push", { data }]);
|
|
4103
4238
|
});
|
|
4104
|
-
this.bind({ blur: "focusout", focus: "focusin" }, (e, type, view, targetEl, phxEvent,
|
|
4105
|
-
if (!
|
|
4239
|
+
this.bind({ blur: "focusout", focus: "focusin" }, (e, type, view, targetEl, phxEvent, phxTarget) => {
|
|
4240
|
+
if (!phxTarget) {
|
|
4106
4241
|
let data = { key: e.key, ...this.eventMeta(type, e, targetEl) };
|
|
4107
4242
|
js_default.exec(type, phxEvent, view, targetEl, ["push", { data }]);
|
|
4108
4243
|
}
|
|
4109
4244
|
});
|
|
4110
|
-
this.bind({ blur: "blur", focus: "focus" }, (e, type, view, targetEl,
|
|
4245
|
+
this.bind({ blur: "blur", focus: "focus" }, (e, type, view, targetEl, phxEvent, phxTarget) => {
|
|
4111
4246
|
if (phxTarget === "window") {
|
|
4112
4247
|
let data = this.eventMeta(type, e, targetEl);
|
|
4113
4248
|
js_default.exec(type, phxEvent, view, targetEl, ["push", { data }]);
|
|
@@ -4188,7 +4323,7 @@ var LiveSocket = class {
|
|
|
4188
4323
|
}
|
|
4189
4324
|
}
|
|
4190
4325
|
bindClicks() {
|
|
4191
|
-
window.addEventListener("
|
|
4326
|
+
window.addEventListener("mousedown", (e) => this.clickStartedAtTarget = e.target);
|
|
4192
4327
|
this.bindClick("click", "click", false);
|
|
4193
4328
|
this.bindClick("mousedown", "capture-click", true);
|
|
4194
4329
|
}
|
|
@@ -4230,7 +4365,7 @@ var LiveSocket = class {
|
|
|
4230
4365
|
if (!(el.isSameNode(clickStartedAt) || el.contains(clickStartedAt))) {
|
|
4231
4366
|
this.withinOwners(e.target, (view) => {
|
|
4232
4367
|
let phxEvent = el.getAttribute(phxClickAway);
|
|
4233
|
-
if (js_default.isVisible(el)) {
|
|
4368
|
+
if (js_default.isVisible(el) && js_default.isInViewport(el)) {
|
|
4234
4369
|
js_default.exec("click", phxEvent, view, el, ["push", { data: this.eventMeta("click", e, e.target) }]);
|
|
4235
4370
|
}
|
|
4236
4371
|
});
|
|
@@ -4279,7 +4414,7 @@ var LiveSocket = class {
|
|
|
4279
4414
|
if (!type || !this.isConnected() || !this.main || dom_default.wantsNewTab(e)) {
|
|
4280
4415
|
return;
|
|
4281
4416
|
}
|
|
4282
|
-
let href = target.href;
|
|
4417
|
+
let href = target.href instanceof SVGAnimatedString ? target.href.baseVal : target.href;
|
|
4283
4418
|
let linkState = target.getAttribute(PHX_LINK_STATE);
|
|
4284
4419
|
e.preventDefault();
|
|
4285
4420
|
e.stopImmediatePropagation();
|