-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
component-ctx.ts
70 lines (60 loc) · 2.52 KB
/
component-ctx.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import { assertDefined } from '../assert/assert';
import type { RenderContext } from '../render/cursor';
import { visitJsxNode } from '../render/render';
import { ComponentScopedStyles, OnRenderProp } from '../util/markers';
import { then } from '../util/promises';
import { styleContent, styleHost } from './qrl-styles';
import { newInvokeContext, useInvoke } from '../use/use-core';
import { getContext, getEvent, QContext } from '../props/props';
import type { JSXNode, ValueOrPromise } from '..';
import { processNode } from '../render/jsx/jsx-runtime';
// TODO(misko): Can we get rid of this whole file, and instead teach getProps to know how to render
// the advantage will be that the render capability would then be exposed to the outside world as well.
export class QComponentCtx {
__brand__!: 'QComponentCtx';
ctx: QContext;
hostElement: HTMLElement;
styleId: string | undefined | null = undefined;
styleClass: string | null = null;
styleHostClass: string | null = null;
slots: JSXNode[] = [];
constructor(hostElement: HTMLElement) {
this.hostElement = hostElement;
this.ctx = getContext(hostElement);
}
render(ctx: RenderContext): ValueOrPromise<void> {
const hostElement = this.hostElement;
const onRender = getEvent(this.ctx, OnRenderProp) as any as () => JSXNode;
assertDefined(onRender);
const event = 'qRender';
this.ctx.dirty = false;
ctx.globalState.hostsStaging.delete(hostElement);
const promise = useInvoke(newInvokeContext(hostElement, hostElement, event), onRender);
return then(promise, (jsxNode) => {
// Types are wrong here
jsxNode = (jsxNode as any)[0];
if (this.styleId === undefined) {
const scopedStyleId = (this.styleId = hostElement.getAttribute(ComponentScopedStyles));
if (scopedStyleId) {
this.styleHostClass = styleHost(scopedStyleId);
this.styleClass = styleContent(scopedStyleId);
}
}
ctx.hostElements.add(hostElement);
this.slots = [];
const newCtx: RenderContext = {
...ctx,
component: this,
};
return visitJsxNode(newCtx, hostElement, processNode(jsxNode), false);
});
}
}
const COMPONENT_PROP = '__qComponent__';
export function getQComponent(hostElement: Element): QComponentCtx | undefined {
const element = hostElement as { [COMPONENT_PROP]?: QComponentCtx };
let component = element[COMPONENT_PROP];
if (!component)
component = element[COMPONENT_PROP] = new QComponentCtx(hostElement as HTMLElement) as any;
return component;
}