Skip to content

Commit

Permalink
feat: implement missing on methods (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
manucorporat authored Mar 12, 2022
1 parent 0b247ca commit 5ace5ad
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 139 deletions.
115 changes: 54 additions & 61 deletions src/core/component/component-ctx.ts
Original file line number Diff line number Diff line change
@@ -1,70 +1,63 @@
import { assertDefined } from '../assert/assert';
import type { RenderContext } from '../render/cursor';
import { visitJsxNode } from '../render/render';
import { ComponentScopedStyles, OnRenderProp } from '../util/markers';
import { ComponentScopedStyles, OnRenderProp, QHostAttr } 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 { getEvent, QContext } from '../props/props';
import type { JSXNode } 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,
export const firstRenderComponent = (rctx: RenderContext, ctx: QContext) => {
ctx.element.setAttribute(QHostAttr, '');
const result = renderComponent(rctx, ctx);
// if (ctx.component?.styleHostClass) {
// classlistAdd(rctx, ctx.element, ctx.component.styleHostClass);
// }
return result;
};

export const renderComponent = (rctx: RenderContext, ctx: QContext) => {
const hostElement = ctx.element as HTMLElement;
const onRender = getEvent(ctx, OnRenderProp) as any as () => JSXNode;
const event = 'qRender';
assertDefined(onRender);

// Component is not dirty any more
ctx.dirty = false;
rctx.globalState.hostsStaging.delete(hostElement);

// Invoke render hook
const promise = useInvoke(newInvokeContext(hostElement, hostElement, event), onRender);

return then(promise, (jsxNode) => {
// Types are wrong here
jsxNode = (jsxNode as any)[0];
rctx.hostElements.add(hostElement);
let componentCtx = ctx.component;
if (!componentCtx) {
componentCtx = ctx.component = {
hostElement,
slots: [],
styleHostClass: undefined,
styleClass: undefined,
styleId: undefined,
};
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;
}
const scopedStyleId = hostElement.getAttribute(ComponentScopedStyles) ?? undefined;
if (scopedStyleId) {
componentCtx.styleId = scopedStyleId;
componentCtx.styleHostClass = styleHost(scopedStyleId);
componentCtx.styleClass = styleContent(scopedStyleId);
hostElement.classList.add(componentCtx.styleHostClass);
}
}
componentCtx.slots = [];

const newCtx: RenderContext = {
...rctx,
component: componentCtx,
};
return visitJsxNode(newCtx, hostElement, processNode(jsxNode), false);
});
};
2 changes: 1 addition & 1 deletion src/core/component/component.public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const onUnmount$ = implicit$FirstArg(onUnmountFromQrl);
*/
// </docs>
export function onResumeFromQrl(resumeFn: QRL<() => void>): void {
throw new Error('IMPLEMENT: onRender' + resumeFn);
onWindow('load', resumeFn);
}

// <docs markdown="https://hackmd.io/c_nNpiLZSYugTU0c5JATJA#onResume">
Expand Down
9 changes: 4 additions & 5 deletions src/core/props/props-obj-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export interface QObjectMap {
}

export function newQObjectMap(element: Element): QObjectMap {
const map = new Map<QObject<any>, number>();
const array: QObject<any>[] = [];
let added = element.hasAttribute(QObjAttr);

Expand All @@ -19,12 +18,12 @@ export function newQObjectMap(element: Element): QObjectMap {
return array[index];
},
indexOf(obj: string): number | undefined {
return map.get(obj);
const index = array.indexOf(obj);
return index === -1 ? undefined : index;
},
add(object: QObject<any>) {
const index = map.get(object);
if (index === undefined) {
map.set(object, array.length);
const index = array.indexOf(object);
if (index === -1) {
array.push(object);
if (!added) {
element.setAttribute(QObjAttr, '');
Expand Down
15 changes: 2 additions & 13 deletions src/core/props/props-on.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { invokeWatchFn } from '../watch/watch';
import { getEvents, QContext } from './props';
import { getDocument } from '../util/dom';
import { RenderContext, setAttribute } from '../render/cursor';
import { emitEvent } from '../util/event';

const ON_PROP_REGEX = /on(Document|Window)?:/;
const ON$_PROP_REGEX = /on(Document|Window)?\$:/;
Expand Down Expand Up @@ -66,7 +67,7 @@ export function qPropReadQRL(
}

context.qrl = qrl;
symbolUsed(ctx.element, qrl.symbol);
emitEvent(ctx.element, 'qSymbol', { name: qrl.symbol }, true);
if (qrlGuard) {
return invokeWatchFn(ctx.element, qrl);
} else {
Expand All @@ -77,18 +78,6 @@ export function qPropReadQRL(
};
}

const symbolUsed = (el: Element, name: string) => {
if (typeof CustomEvent === 'function') {
el.dispatchEvent(
new CustomEvent('qSymbol', {
detail: { name },
bubbles: true,
composed: true,
})
);
}
};

export function qPropWriteQRL(
rctx: RenderContext | undefined,
ctx: QContext,
Expand Down
11 changes: 11 additions & 0 deletions src/core/props/props.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { JSXNode } from '..';
import { QError, qError } from '../error/error';
import { getProxyMap, readWriteProxy } from '../object/q-object';
import { QStore_hydrate } from '../object/store';
Expand Down Expand Up @@ -25,13 +26,22 @@ export interface QContextEvents {
[eventName: string]: string | undefined;
}

export interface ComponentCtx {
hostElement: HTMLElement;
styleId: string | undefined;
styleClass: string | undefined;
styleHostClass: string | undefined;
slots: JSXNode[];
}

export interface QContext {
cache: Map<string, any>;
refMap: QObjectMap;
element: Element;
dirty: boolean;
props: Record<string, any> | undefined;
events: QContextEvents | undefined;
component: ComponentCtx | undefined;
}

export function getContext(element: Element): QContext {
Expand All @@ -45,6 +55,7 @@ export function getContext(element: Element): QContext {
dirty: false,
props: undefined,
events: undefined,
component: undefined,
};
}
return ctx;
Expand Down
Loading

0 comments on commit 5ace5ad

Please sign in to comment.