forked from mystiq/hydrogen-web
first draft of generic popup and menu views
This commit is contained in:
parent
6fd10b63e5
commit
9bb521986b
6 changed files with 645 additions and 6 deletions
378
prototypes/menu-relative.html
Normal file
378
prototypes/menu-relative.html
Normal file
|
@ -0,0 +1,378 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template: "left middle" 1fr /
|
||||
200px 1fr;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.container .left {
|
||||
display: grid;
|
||||
grid-template:
|
||||
"welcome" auto
|
||||
"rooms" 1fr /
|
||||
1fr;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.container .middle {
|
||||
display: grid;
|
||||
grid-template:
|
||||
"header" auto
|
||||
"timeline" 1fr
|
||||
"composer" auto /
|
||||
1fr;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.left { grid-area: left;}
|
||||
.left p {
|
||||
grid-area welcome;
|
||||
display: flex;
|
||||
}
|
||||
.left ul {
|
||||
grid-area: rooms;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.middle { grid-area: middle;}
|
||||
.middle .header { grid-area: header;}
|
||||
.middle .timeline {
|
||||
grid-area: timeline;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.middle .composer {
|
||||
grid-area: composer;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.composer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.composer input {
|
||||
display: block;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
box-shadow: 2px 2px 10px rgba(0,0,0,0.5);
|
||||
padding: 16px;
|
||||
background-color: white;
|
||||
z-index: 1;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="left">
|
||||
<p>Welcome!<button>⋮</button></p>
|
||||
<ul>
|
||||
<li>Room xyz <button>⋮</button></li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz <button>⋮</button></li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz</li>
|
||||
<li>Room xyz <button>⋮</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="middle">
|
||||
<div class="header">
|
||||
<h2>Room xyz</h2>
|
||||
<button>⋮</button>
|
||||
</div>
|
||||
<ul class="timeline">
|
||||
<li>Message abc</li>
|
||||
<li>Message abc <button>⋮</button></li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc <button>⋮</button></li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc</li>
|
||||
<li>Message abc <button>⋮</button></li>
|
||||
</ul>
|
||||
<div class="composer">
|
||||
<input type="text" name="">
|
||||
<button>⋮</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
let menu;
|
||||
|
||||
function createMenu(options) {
|
||||
const menu = document.createElement("ul");
|
||||
menu.className = "menu";
|
||||
for (const o of options) {
|
||||
const li = document.createElement("li");
|
||||
li.innerText = o;
|
||||
menu.appendChild(li);
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
function showMenu(evt) {
|
||||
if (menu) {
|
||||
menu = menu.close();
|
||||
} else if (evt.target.tagName.toLowerCase() === "button") {
|
||||
menu = showPopup(evt.target, createMenu(["Send file", "Save contact", "Send picture", "Foo the bar"]), {
|
||||
horizontal: {
|
||||
relativeTo: "end",
|
||||
align: "start",
|
||||
after: 0,
|
||||
},
|
||||
vertical: {
|
||||
relativeTo: "end",
|
||||
align: "end",
|
||||
after: 10,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function showMenuInScroller(evt) {
|
||||
if (!menu && evt.target.tagName.toLowerCase() === "button") {
|
||||
evt.stopPropagation();
|
||||
menu = showPopup(evt.target, createMenu(["Show reactions", "Share"]), {
|
||||
horizontal: {
|
||||
relativeTo: "start",
|
||||
align: "end",
|
||||
after: 10,
|
||||
},
|
||||
vertical: {
|
||||
relativeTo: "start",
|
||||
align: "center",
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.body.addEventListener("click", showMenu, false);
|
||||
document.querySelector(".middle ul").addEventListener("click", showMenuInScroller, false);
|
||||
document.querySelector(".left ul").addEventListener("click", showMenuInScroller, false);
|
||||
|
||||
function showPopup(target, popup, arrangement) {
|
||||
targetAxes = elementToAxes(target);
|
||||
if (!arrangement) {
|
||||
arrangement = getAutoArrangement(targetAxes);
|
||||
}
|
||||
|
||||
target.offsetParent.appendChild(popup);
|
||||
|
||||
const popupAxes = elementToAxes(popup);
|
||||
const scrollerAxes = elementToAxes(findScrollParent(target));
|
||||
const offsetParentAxes = elementToAxes(target.offsetParent);
|
||||
|
||||
function reposition() {
|
||||
if (scrollerAxes && !isVisibleInScrollParent(targetAxes.vertical, scrollerAxes.vertical)) {
|
||||
popupObj.close();
|
||||
}
|
||||
applyArrangement(
|
||||
popupAxes.vertical,
|
||||
targetAxes.vertical,
|
||||
offsetParentAxes.vertical,
|
||||
scrollerAxes?.vertical,
|
||||
arrangement.vertical
|
||||
);
|
||||
applyArrangement(
|
||||
popupAxes.horizontal,
|
||||
targetAxes.horizontal,
|
||||
offsetParentAxes.horizontal,
|
||||
scrollerAxes?.horizontal,
|
||||
arrangement.horizontal
|
||||
);
|
||||
}
|
||||
reposition();
|
||||
|
||||
document.body.addEventListener("scroll", reposition, true);
|
||||
|
||||
const popupObj = {
|
||||
close() {
|
||||
document.body.removeEventListener("scroll", reposition, true);
|
||||
popup.remove();
|
||||
}
|
||||
};
|
||||
|
||||
return popupObj;
|
||||
}
|
||||
|
||||
function elementToAxes(element) {
|
||||
if (element) {
|
||||
return {
|
||||
horizontal: new HorizontalAxis(element),
|
||||
vertical: new VerticalAxis(element),
|
||||
element
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function findScrollParent(el) {
|
||||
let parent = el;
|
||||
do {
|
||||
parent = parent.parentElement;
|
||||
if (parent.scrollHeight > parent.clientHeight) {
|
||||
return parent;
|
||||
}
|
||||
} while (parent !== el.offsetParent);
|
||||
}
|
||||
|
||||
function isVisibleInScrollParent(targetAxis, scrollerAxis) {
|
||||
// clipped at start?
|
||||
if ((targetAxis.offsetStart + targetAxis.clientSize) < (
|
||||
scrollerAxis.offsetStart +
|
||||
scrollerAxis.scrollOffset
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
// clipped at end?
|
||||
if (targetAxis.offsetStart > (
|
||||
scrollerAxis.offsetStart +
|
||||
scrollerAxis.clientSize +
|
||||
scrollerAxis.scrollOffset
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyArrangement(elAxis, targetAxis, offsetParentAxis, scrollerAxis, {relativeTo, align, before, after}) {
|
||||
if (relativeTo === "end") {
|
||||
let end = offsetParentAxis.clientSize - targetAxis.offsetStart;
|
||||
if (align === "end") {
|
||||
end -= elAxis.offsetSize;
|
||||
} else if (align === "center") {
|
||||
end -= ((elAxis.offsetSize / 2) - (targetAxis.offsetSize / 2));
|
||||
}
|
||||
if (typeof before === "number") {
|
||||
end += before;
|
||||
} else if (typeof after === "number") {
|
||||
end -= (targetAxis.offsetSize + after);
|
||||
}
|
||||
elAxis.end = end;
|
||||
} else if (relativeTo === "start") {
|
||||
let scrollOffset = scrollerAxis?.scrollOffset || 0;
|
||||
let start = targetAxis.offsetStart - scrollOffset;
|
||||
if (align === "start") {
|
||||
start -= elAxis.offsetSize;
|
||||
} else if (align === "center") {
|
||||
start -= ((elAxis.offsetSize / 2) - (targetAxis.offsetSize / 2));
|
||||
}
|
||||
if (typeof before === "number") {
|
||||
start -= before;
|
||||
} else if (typeof after === "number") {
|
||||
start += (targetAxis.offsetSize + after);
|
||||
}
|
||||
elAxis.start = start;
|
||||
} else {
|
||||
throw new Error("unknown relativeTo: " + relativeTo);
|
||||
}
|
||||
}
|
||||
|
||||
class HorizontalAxis {
|
||||
constructor(el) {
|
||||
this.element = el;
|
||||
}
|
||||
get scrollOffset() {return this.element.scrollLeft;}
|
||||
get clientSize() {return this.element.clientWidth;}
|
||||
get offsetSize() {return this.element.offsetWidth;}
|
||||
get offsetStart() {return this.element.offsetLeft;}
|
||||
set start(value) {this.element.style.left = `${value}px`;}
|
||||
set end(value) {this.element.style.right = `${value}px`;}
|
||||
}
|
||||
class VerticalAxis {
|
||||
constructor(el) {
|
||||
this.element = el;
|
||||
}
|
||||
get scrollOffset() {return this.element.scrollTop;}
|
||||
get clientSize() {return this.element.clientHeight;}
|
||||
get offsetSize() {return this.element.offsetHeight;}
|
||||
get offsetStart() {return this.element.offsetTop;}
|
||||
set start(value) {this.element.style.top = `${value}px`;}
|
||||
set end(value) {this.element.style.bottom = `${value}px`;}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -96,6 +96,8 @@ main {
|
|||
width: 100%;
|
||||
/* otherwise we don't get scrollbars and the content grows as large as it can */
|
||||
min-height: 0;
|
||||
/* make popups relative to this element so changing the left panel width doesn't affect their position */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.RoomView {
|
||||
|
@ -163,3 +165,8 @@ main {
|
|||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
|
@ -762,4 +762,31 @@ button.link {
|
|||
width: 200px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
border-radius: 8px;
|
||||
box-shadow: 2px 2px 10px rgba(0,0,0,0.5);
|
||||
padding: 4px;
|
||||
background-color: white;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.menu button {
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
border: none;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
text-align: left;
|
||||
padding: 8px 32px 8px 8px;
|
||||
}
|
||||
|
||||
.menu button:focus {
|
||||
background-color: #03B381;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.menu button:hover {
|
||||
background-color: #03B381;
|
||||
color: white;
|
||||
}
|
||||
|
|
49
src/platform/web/ui/general/Menu.js
Normal file
49
src/platform/web/ui/general/Menu.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {TemplateView} from "./TemplateView.js";
|
||||
|
||||
export class Menu extends TemplateView {
|
||||
static option(label, callback) {
|
||||
return new MenuOption(label, callback);
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
super();
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
render(t) {
|
||||
return t.ul({className: "menu", role: "menu"}, this._options.map(o => {
|
||||
return t.li({
|
||||
className: o.icon ? `icon ${o.icon}` : "",
|
||||
}, t.button({onClick: o.callback}, o.label));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class MenuOption {
|
||||
constructor(label, callback) {
|
||||
this.label = label;
|
||||
this.callback = callback;
|
||||
this.icon = null;
|
||||
}
|
||||
|
||||
setIcon(className) {
|
||||
this.icon = className;
|
||||
return this;
|
||||
}
|
||||
}
|
174
src/platform/web/ui/general/Popup.js
Normal file
174
src/platform/web/ui/general/Popup.js
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
const HorizontalAxis = {
|
||||
scrollOffset(el) {return el.scrollLeft;},
|
||||
size(el) {return el.offsetWidth;},
|
||||
offsetStart(el) {return el.offsetLeft;},
|
||||
setStart(el, value) {el.style.left = `${value}px`;},
|
||||
setEnd(el, value) {el.style.right = `${value}px`;},
|
||||
};
|
||||
const VerticalAxis = {
|
||||
scrollOffset(el) {return el.scrollTop;},
|
||||
size(el) {return el.offsetHeight;},
|
||||
offsetStart(el) {return el.offsetTop;},
|
||||
setStart(el, value) {el.style.top = `${value}px`;},
|
||||
setEnd(el, value) {el.style.bottom = `${value}px`;},
|
||||
};
|
||||
|
||||
export class Popup {
|
||||
constructor(view) {
|
||||
this._view = view;
|
||||
this._target = null;
|
||||
this._arrangement = null;
|
||||
this._scroller = null;
|
||||
this._fakeRoot = null;
|
||||
this._trackingTemplateView = null;
|
||||
}
|
||||
|
||||
trackInTemplateView(templateView) {
|
||||
this._trackingTemplateView = templateView;
|
||||
this._trackingTemplateView.addSubView(this);
|
||||
}
|
||||
|
||||
showRelativeTo(target, arrangement) {
|
||||
this._target = target;
|
||||
this._arrangement = arrangement;
|
||||
this._scroller = findScrollParent(this._target);
|
||||
this._view.mount();
|
||||
this._target.offsetParent.appendChild(this._popup);
|
||||
this._applyArrangementAxis(HorizontalAxis, this._arrangement.horizontal);
|
||||
this._applyArrangementAxis(VerticalAxis, this._arrangement.vertical);
|
||||
if (this._scroller) {
|
||||
document.body.addEventListener("scroll", this, true);
|
||||
}
|
||||
setTimeout(() => {
|
||||
document.body.addEventListener("click", this, false);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
close() {
|
||||
this._view.unmount();
|
||||
this._trackingTemplateView.removeSubView(this);
|
||||
if (this._scroller) {
|
||||
document.body.removeEventListener("scroll", this, true);
|
||||
}
|
||||
document.body.removeEventListener("click", this, false);
|
||||
this._popup.remove();
|
||||
}
|
||||
|
||||
get _popup() {
|
||||
return this._view.root();
|
||||
}
|
||||
|
||||
handleEvent(evt) {
|
||||
if (evt.type === "scroll") {
|
||||
this._onScroll();
|
||||
} else if (evt.type === "click") {
|
||||
this._onClick(evt);
|
||||
}
|
||||
}
|
||||
|
||||
_onScroll() {
|
||||
if (this._scroller && !this._isVisibleInScrollParent(VerticalAxis)) {
|
||||
this.close();
|
||||
}
|
||||
this._applyArrangementAxis(HorizontalAxis, this._arrangement.horizontal);
|
||||
this._applyArrangementAxis(VerticalAxis, this._arrangement.vertical);
|
||||
}
|
||||
|
||||
_onClick() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
_applyArrangementAxis(axis, {relativeTo, align, before, after}) {
|
||||
if (relativeTo === "end") {
|
||||
let end = axis.size(this._target.offsetParent) - axis.offsetStart(this._target);
|
||||
if (align === "end") {
|
||||
end -= axis.size(this._popup);
|
||||
} else if (align === "center") {
|
||||
end -= ((axis.size(this._popup) / 2) - (axis.size(this._target) / 2));
|
||||
}
|
||||
if (typeof before === "number") {
|
||||
end += before;
|
||||
} else if (typeof after === "number") {
|
||||
end -= (axis.size(this._target) + after);
|
||||
}
|
||||
axis.setEnd(this._popup, end);
|
||||
} else if (relativeTo === "start") {
|
||||
let scrollOffset = this._scroller ? axis.scrollOffset(this._scroller) : 0;
|
||||
let start = axis.offsetStart(this._target) - scrollOffset;
|
||||
if (align === "start") {
|
||||
start -= axis.size(this._popup);
|
||||
} else if (align === "center") {
|
||||
start -= ((axis.size(this._popup) / 2) - (axis.size(this._target) / 2));
|
||||
}
|
||||
if (typeof before === "number") {
|
||||
start -= before;
|
||||
} else if (typeof after === "number") {
|
||||
start += (axis.size(this._target) + after);
|
||||
}
|
||||
axis.setStart(this._popup, start);
|
||||
} else {
|
||||
throw new Error("unknown relativeTo: " + relativeTo);
|
||||
}
|
||||
}
|
||||
|
||||
_isVisibleInScrollParent(axis) {
|
||||
// clipped at start?
|
||||
if ((axis.offsetStart(this._target) + axis.size(this._target)) < (
|
||||
axis.offsetStart(this._scroller) +
|
||||
axis.scrollOffset(this._scroller)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
// clipped at end?
|
||||
if (axis.offsetStart(this._target) > (
|
||||
axis.offsetStart(this._scroller) +
|
||||
axis.size(this._scroller) +
|
||||
axis.scrollOffset(this._scroller)
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* fake UIView api, so it can be tracked by a template view as a subview */
|
||||
root() {
|
||||
return this._fakeRoot;
|
||||
}
|
||||
|
||||
mount() {
|
||||
this._fakeRoot = document.createComment("popup");
|
||||
return this._fakeRoot;
|
||||
}
|
||||
|
||||
unmount() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
update() {}
|
||||
}
|
||||
|
||||
function findScrollParent(el) {
|
||||
let parent = el;
|
||||
do {
|
||||
parent = parent.parentElement;
|
||||
if (parent.scrollHeight > parent.clientHeight) {
|
||||
return parent;
|
||||
}
|
||||
} while (parent !== el.offsetParent);
|
||||
}
|
|
@ -44,9 +44,6 @@ export class TemplateView {
|
|||
this._render = render;
|
||||
this._eventListeners = null;
|
||||
this._bindings = null;
|
||||
// this should become _subViews and also include templates.
|
||||
// How do we know which ones we should update though?
|
||||
// Wrapper class?
|
||||
this._subViews = null;
|
||||
this._root = null;
|
||||
this._boundUpdateFromValue = null;
|
||||
|
@ -57,7 +54,7 @@ export class TemplateView {
|
|||
}
|
||||
|
||||
_subscribe() {
|
||||
if (typeof this._value.on === "function") {
|
||||
if (typeof this._value?.on === "function") {
|
||||
this._boundUpdateFromValue = this._updateFromValue.bind(this);
|
||||
this._value.on("change", this._boundUpdateFromValue);
|
||||
}
|
||||
|
@ -146,12 +143,19 @@ export class TemplateView {
|
|||
this._bindings.push(bindingFn);
|
||||
}
|
||||
|
||||
_addSubView(view) {
|
||||
addSubView(view) {
|
||||
if (!this._subViews) {
|
||||
this._subViews = [];
|
||||
}
|
||||
this._subViews.push(view);
|
||||
}
|
||||
|
||||
removeSubView(view) {
|
||||
const idx = this._subViews.indexOf(view);
|
||||
if (idx !== -1) {
|
||||
this._subViews.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// what is passed to render
|
||||
|
@ -288,7 +292,7 @@ class TemplateBuilder {
|
|||
} catch (err) {
|
||||
return errorToDOM(err);
|
||||
}
|
||||
this._templateView._addSubView(view);
|
||||
this._templateView.addSubView(view);
|
||||
return root;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue