forked from mystiq/hydrogen-web
Rough changes to make render to string work
This commit is contained in:
parent
5805ce0310
commit
8356fbf70f
5 changed files with 150 additions and 48 deletions
|
@ -81,7 +81,7 @@ export class ListView<T, V extends IView> implements IView, IListObserver<T> {
|
|||
const root = this._root = el(this._tagName, attr);
|
||||
this.loadList();
|
||||
if (this._onItemClick) {
|
||||
root.addEventListener("click", this);
|
||||
//root.addEventListener("click", this);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ export abstract class TemplateView<T extends IObservableValue> extends BaseUpdat
|
|||
const builder = new TemplateBuilder(this) as Builder<T>;
|
||||
try {
|
||||
this._root = this.render(builder, this._value);
|
||||
console.log('this._root', this._root)
|
||||
} finally {
|
||||
builder.close();
|
||||
}
|
||||
|
@ -288,6 +289,49 @@ export class TemplateBuilder<T extends IObservableValue> {
|
|||
|
||||
const node = document.createElementNS(ns, name);
|
||||
|
||||
const attrMap = {};
|
||||
if(attributes) {
|
||||
for(let [key, value] of Object.entries(attributes)) {
|
||||
// binding for className as object of className => enabled
|
||||
if (typeof value === "object") {
|
||||
if (key !== "className" || value === null) {
|
||||
// Ignore non-className objects.
|
||||
continue;
|
||||
}
|
||||
if (objHasFns(value)) {
|
||||
//this._addClassNamesBinding(node, value);
|
||||
attrMap[key] = classNames(value, value);
|
||||
} else {
|
||||
attrMap[key] = classNames(value, this._value);
|
||||
}
|
||||
} else if (this._isEventHandler(key, value)) {
|
||||
// no-op
|
||||
} else if (typeof value === "function") {
|
||||
this._addAttributeBinding(node, key, value);
|
||||
} else {
|
||||
attrMap[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const attrString = Object.keys(attrMap)
|
||||
.map((attrKey) => {
|
||||
return `${attrKey}="${attrMap[attrKey]}"`;
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
const childenStrings = [];
|
||||
for (let child of [].concat(children)) {
|
||||
console.log('child', child)
|
||||
if (typeof child === "function") {
|
||||
//child = this._addTextBinding(child);
|
||||
childenStrings.push('todo');
|
||||
}
|
||||
childenStrings.push(child);
|
||||
}
|
||||
|
||||
return `<${name} ${attrString}>${childenStrings.join('')}</${name}>`;
|
||||
|
||||
if (attributes) {
|
||||
this._setNodeAttributes(node, attributes);
|
||||
}
|
||||
|
@ -303,6 +347,8 @@ export class TemplateBuilder<T extends IObservableValue> {
|
|||
view(view: IView, mountOptions?: IMountArgs): ViewNode {
|
||||
this._templateView.addSubView(view);
|
||||
return mountView(view, mountOptions);
|
||||
|
||||
//return view.render(this, this._value)
|
||||
}
|
||||
|
||||
// map a value to a view, every time the value changes
|
||||
|
@ -322,7 +368,8 @@ export class TemplateBuilder<T extends IObservableValue> {
|
|||
if (view) {
|
||||
return this.view(view);
|
||||
} else {
|
||||
return document.createComment("node binding placeholder");
|
||||
//return document.createComment("node binding placeholder");
|
||||
return '<!-- node binding placeholder -->';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -337,7 +384,8 @@ export class TemplateBuilder<T extends IObservableValue> {
|
|||
if (!rootNode) {
|
||||
// TODO: this will confuse mapView which assumes that
|
||||
// a comment node means there is no view to clean up
|
||||
return document.createComment("map placeholder");
|
||||
//return document.createComment("map placeholder");
|
||||
return '<!-- map placeholder -->';
|
||||
}
|
||||
return rootNode;
|
||||
});
|
||||
|
@ -365,16 +413,16 @@ export class TemplateBuilder<T extends IObservableValue> {
|
|||
You should not call the TemplateBuilder (e.g. `t.xxx()`) at all from the side effect,
|
||||
instead use tags from html.ts to help you construct any DOM you need. */
|
||||
mapSideEffect<R>(mapFn: (value: T) => R, sideEffect: (newV: R, oldV: R | undefined) => void) {
|
||||
let prevValue = mapFn(this._value);
|
||||
const binding = () => {
|
||||
const newValue = mapFn(this._value);
|
||||
if (prevValue !== newValue) {
|
||||
sideEffect(newValue, prevValue);
|
||||
prevValue = newValue;
|
||||
}
|
||||
};
|
||||
this._addBinding(binding);
|
||||
sideEffect(prevValue, undefined);
|
||||
// let prevValue = mapFn(this._value);
|
||||
// const binding = () => {
|
||||
// const newValue = mapFn(this._value);
|
||||
// if (prevValue !== newValue) {
|
||||
// sideEffect(newValue, prevValue);
|
||||
// prevValue = newValue;
|
||||
// }
|
||||
// };
|
||||
// this._addBinding(binding);
|
||||
// sideEffect(prevValue, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ export function el(elementName: string, attributes?: BasicAttributes<never> | Ch
|
|||
}
|
||||
|
||||
export function elNS(ns: string, elementName: string, attributes?: BasicAttributes<never> | Child | Child[], children?: Child | Child[]): Element {
|
||||
//console.log('html elNS', new Error().stack);
|
||||
if (attributes && isChildren(attributes)) {
|
||||
children = attributes;
|
||||
attributes = undefined;
|
||||
|
@ -67,6 +68,27 @@ export function elNS(ns: string, elementName: string, attributes?: BasicAttribut
|
|||
|
||||
const e = document.createElementNS(ns, elementName);
|
||||
|
||||
const attrMap = {};
|
||||
if (attributes) {
|
||||
for (let [name, value] of Object.entries(attributes)) {
|
||||
if (typeof value === "object") {
|
||||
// Only className should ever be an object; be careful
|
||||
// here anyway and ignore object-valued non-className attributes.
|
||||
value = (value !== null && name === "className") ? classNames(value, undefined) : false;
|
||||
}
|
||||
attrMap[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const attrString = Object.keys(attrMap)
|
||||
.map((attrKey) => {
|
||||
return `${attrKey}="${attrMap[attrKey]}"`;
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
return `<${elementName} ${attrString}>${[].concat(children).join('')}</${elementName}>`;
|
||||
|
||||
|
||||
if (attributes) {
|
||||
for (let [name, value] of Object.entries(attributes)) {
|
||||
if (typeof value === "object") {
|
||||
|
@ -93,7 +115,8 @@ export function elNS(ns: string, elementName: string, attributes?: BasicAttribut
|
|||
}
|
||||
|
||||
export function text(str: string): Text {
|
||||
return document.createTextNode(str);
|
||||
return str;
|
||||
//return document.createTextNode(str);
|
||||
}
|
||||
|
||||
export const HTML_NS: string = "http://www.w3.org/1999/xhtml";
|
||||
|
|
|
@ -57,16 +57,34 @@ export class TimelineView extends TemplateView<TimelineViewModel> {
|
|||
|
||||
render(t: Builder<TimelineViewModel>, vm: TimelineViewModel) {
|
||||
// assume this view will be mounted in the parent DOM straight away
|
||||
requestAnimationFrame(() => {
|
||||
// do initial scroll positioning
|
||||
this.restoreScrollPosition();
|
||||
});
|
||||
// requestAnimationFrame(() => {
|
||||
// // do initial scroll positioning
|
||||
// this.restoreScrollPosition();
|
||||
// });
|
||||
console.log('vm.tiles', vm.tiles)
|
||||
|
||||
const childrenRenders = [];
|
||||
for(const entry of vm.tiles) {
|
||||
const View = viewClassForEntry(entry);
|
||||
if (View) {
|
||||
const view = new View(entry);
|
||||
const childrenRender = view.render(t, entry);
|
||||
console.log('childrenRender', childrenRender)
|
||||
childrenRenders.push(childrenRender);
|
||||
//childrenViews.push();
|
||||
}
|
||||
}
|
||||
|
||||
this.tilesView = new TilesListView(vm.tiles, () => this.restoreScrollPosition());
|
||||
const root = t.div({className: "Timeline"}, [
|
||||
t.div({
|
||||
className: "Timeline_scroller bottom-aligned-scroll",
|
||||
onScroll: () => this.onScroll()
|
||||
}, t.view(this.tilesView)),
|
||||
t.div(
|
||||
{
|
||||
className: "Timeline_scroller bottom-aligned-scroll",
|
||||
onScroll: () => this.onScroll()
|
||||
},
|
||||
//t.view(this.tilesView)
|
||||
childrenRenders
|
||||
),
|
||||
t.button({
|
||||
className: {
|
||||
"Timeline_jumpDown": true,
|
||||
|
@ -77,12 +95,12 @@ export class TimelineView extends TemplateView<TimelineViewModel> {
|
|||
})
|
||||
]);
|
||||
|
||||
if (typeof ResizeObserver === "function") {
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
this.restoreScrollPosition();
|
||||
});
|
||||
this.resizeObserver.observe(root);
|
||||
}
|
||||
// if (typeof ResizeObserver === "function") {
|
||||
// this.resizeObserver = new ResizeObserver(() => {
|
||||
// this.restoreScrollPosition();
|
||||
// });
|
||||
// this.resizeObserver.observe(root);
|
||||
// }
|
||||
|
||||
return root;
|
||||
}
|
||||
|
|
|
@ -21,26 +21,36 @@ import {ReplyPreviewError, ReplyPreviewView} from "./ReplyPreviewView.js";
|
|||
export class TextMessageView extends BaseMessageView {
|
||||
renderMessageBody(t, vm) {
|
||||
const time = t.time({className: {hidden: !vm.date}}, vm.date + " " + vm.time);
|
||||
const container = t.div({
|
||||
className: {
|
||||
"Timeline_messageBody": true,
|
||||
statusMessage: vm => vm.shape === "message-status",
|
||||
}
|
||||
}, t.mapView(vm => vm.replyTile, replyTile => {
|
||||
if (this._isReplyPreview) {
|
||||
// if this._isReplyPreview = true, this is already a reply preview, don't nest replies for now.
|
||||
return null;
|
||||
}
|
||||
else if (vm.isReply && !replyTile) {
|
||||
return new ReplyPreviewError();
|
||||
}
|
||||
else if (replyTile) {
|
||||
return new ReplyPreviewView(replyTile);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
|
||||
const parts = [];
|
||||
for (const part of vm.body.parts) {
|
||||
parts.push(renderPart(part));
|
||||
}
|
||||
|
||||
const container = t.div(
|
||||
{
|
||||
className: {
|
||||
"Timeline_messageBody": true,
|
||||
statusMessage: vm => vm.shape === "message-status",
|
||||
}
|
||||
},
|
||||
parts,
|
||||
// t.mapView(vm => vm.replyTile, replyTile => {
|
||||
// if (this._isReplyPreview) {
|
||||
// // if this._isReplyPreview = true, this is already a reply preview, don't nest replies for now.
|
||||
// return null;
|
||||
// }
|
||||
// else if (vm.isReply && !replyTile) {
|
||||
// return new ReplyPreviewError();
|
||||
// }
|
||||
// else if (replyTile) {
|
||||
// return new ReplyPreviewView(replyTile);
|
||||
// }
|
||||
// else {
|
||||
// return null;
|
||||
// }
|
||||
// })
|
||||
);
|
||||
|
||||
const shouldRemove = (element) => element?.nodeType === Node.ELEMENT_NODE && element.className !== "ReplyPreviewView";
|
||||
|
||||
|
@ -54,6 +64,9 @@ export class TextMessageView extends BaseMessageView {
|
|||
container.appendChild(time);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue