mirror of
https://github.com/nvms/soma3.git
synced 2025-12-13 14:40:52 +00:00
neat
This commit is contained in:
parent
4e18b46c89
commit
bceca3053c
118
public/demo.js
118
public/demo.js
@ -68,16 +68,6 @@ function findTemplateNodes(element) {
|
||||
findTemplates(element);
|
||||
return templates;
|
||||
}
|
||||
var nextTick = async (f) => {
|
||||
await new Promise(
|
||||
(r) => setTimeout(
|
||||
(_) => requestAnimationFrame((_2) => {
|
||||
f && f();
|
||||
r();
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
function html(strings, ...values) {
|
||||
const selfClosingTags = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"];
|
||||
let result = strings.reduce((acc, str, i) => acc + str + (values[i] || ""), "");
|
||||
@ -99,6 +89,9 @@ function insertAfter(newNode, existingNode) {
|
||||
existingNode?.parentNode?.appendChild(newNode);
|
||||
}
|
||||
}
|
||||
function insertBefore(newNode, existingNode) {
|
||||
existingNode.parentNode?.insertBefore(newNode, existingNode);
|
||||
}
|
||||
function isPropAttribute(attrName) {
|
||||
if (attrName.startsWith(".")) {
|
||||
return true;
|
||||
@ -222,7 +215,6 @@ var AttributeDirective = class {
|
||||
this.element.classList.remove(c);
|
||||
});
|
||||
} else if (typeof value === "object" && this.extractedAttributeName === "style") {
|
||||
console.log("value is object", value);
|
||||
const next = Object.keys(value);
|
||||
const rm = Object.keys(this.previousStyles).filter((style) => !next.includes(style));
|
||||
next.forEach((style) => {
|
||||
@ -524,31 +516,40 @@ var ShowDirective = class {
|
||||
};
|
||||
|
||||
// src/directives/teleport.ts
|
||||
function _teleport(el, exp, ctx) {
|
||||
const anchor = new Comment(":teleport");
|
||||
el.replaceWith(anchor);
|
||||
function _teleport(el, exp, ctx, component, componentProps, allProps) {
|
||||
const anchor = new Comment(":teleport anchor");
|
||||
insertBefore(anchor, el);
|
||||
const observed = new Comment(":teleport");
|
||||
el.replaceWith(observed);
|
||||
console.log("Creating new block with allProps", component);
|
||||
const target = document.querySelector(exp);
|
||||
if (!target) {
|
||||
console.warn(`teleport target not found: ${exp}`);
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
target.appendChild(el);
|
||||
const observer = new MutationObserver((mutationsList) => {
|
||||
mutationsList.forEach((mutation) => {
|
||||
mutation.removedNodes.forEach((removedNode) => {
|
||||
if (removedNode.contains(anchor)) {
|
||||
el.remove();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
const originalDisplay = el.style.display;
|
||||
el.style.display = "none";
|
||||
let block;
|
||||
target.appendChild(el);
|
||||
const observer = new MutationObserver((mutationsList) => {
|
||||
mutationsList.forEach((mutation) => {
|
||||
mutation.removedNodes.forEach((removedNode) => {
|
||||
if (removedNode.contains(observed)) {
|
||||
if (block.element) block.remove();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
new Block({
|
||||
element: el,
|
||||
parentContext: ctx
|
||||
});
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
el.style.display = originalDisplay;
|
||||
block = new Block({
|
||||
element: el,
|
||||
parentContext: ctx,
|
||||
replacementType: "replace",
|
||||
component,
|
||||
componentProps,
|
||||
allProps
|
||||
});
|
||||
return anchor;
|
||||
}
|
||||
@ -729,6 +730,18 @@ var $computed = Symbol("computed");
|
||||
function isComputed(value) {
|
||||
return isObject(value) && value[$computed];
|
||||
}
|
||||
function computed(getter) {
|
||||
const ref2 = {
|
||||
get value() {
|
||||
return getter();
|
||||
},
|
||||
[$computed]: true
|
||||
};
|
||||
effect(() => {
|
||||
getter();
|
||||
});
|
||||
return ref2;
|
||||
}
|
||||
|
||||
// src/reactivity/ref.ts
|
||||
var $ref = Symbol("ref");
|
||||
@ -848,7 +861,7 @@ var App2 = class {
|
||||
const root = typeof target === "string" ? document.querySelector(target) : target;
|
||||
const display = root.style.display;
|
||||
root.style.display = "none";
|
||||
this.root = this._mount(component, root, props);
|
||||
this._mount(component, root, props);
|
||||
root.style.display = display;
|
||||
return this.root;
|
||||
}
|
||||
@ -861,6 +874,7 @@ var App2 = class {
|
||||
parentContext.scope.$isRef = isRef;
|
||||
parentContext.scope.$isComputed = isComputed;
|
||||
const block = new Block({
|
||||
app: this,
|
||||
element: target,
|
||||
parentContext,
|
||||
component,
|
||||
@ -877,8 +891,7 @@ var App2 = class {
|
||||
function createContext({ parentContext, app: app2 }) {
|
||||
const context = {
|
||||
app: app2 ? app2 : parentContext && parentContext.app ? parentContext.app : null,
|
||||
// scope: parentContext ? parentContext.scope : reactive({}),
|
||||
scope: reactive({}),
|
||||
scope: parentContext ? parentContext.scope : reactive({}),
|
||||
blocks: [],
|
||||
effects: [],
|
||||
slots: [],
|
||||
@ -963,10 +976,11 @@ var Block = class {
|
||||
}
|
||||
if (opts.isRoot) {
|
||||
this.context = opts.parentContext;
|
||||
opts.app.root = this;
|
||||
} else {
|
||||
this.parentContext = opts.parentContext ? opts.parentContext : createContext({});
|
||||
this.parentContext.blocks.push(this);
|
||||
this.context = createContext({ parentContext: opts.parentContext });
|
||||
this.context = createContext({ parentContext: opts.parentContext, app: opts.app });
|
||||
}
|
||||
if (opts.component) {
|
||||
this.componentProps = mergeProps(opts.componentProps ?? {}, opts.component.props ?? {});
|
||||
@ -1101,23 +1115,23 @@ function walk(node, context) {
|
||||
let exp;
|
||||
const handleDirectives = (node2, context2, component, componentProps, allProps) => {
|
||||
if (warnInvalidDirectives(node2, [":if", ":for"])) return;
|
||||
if (warnInvalidDirectives(node2, [":if", ":teleport"])) return;
|
||||
if (warnInvalidDirectives(node2, [":for", ":teleport"])) return;
|
||||
if (warnInvalidDirectives(node2, [":if", ":teleport"])) return;
|
||||
if (exp = checkAndRemoveAttribute(node2, ":scope")) {
|
||||
const scope = evalGet(context2.scope, exp, node2);
|
||||
if (typeof scope === "object") {
|
||||
Object.assign(context2.scope, scope);
|
||||
}
|
||||
}
|
||||
if (exp = checkAndRemoveAttribute(node2, ":teleport")) {
|
||||
return _teleport(node2, exp, context2);
|
||||
}
|
||||
if (exp = checkAndRemoveAttribute(node2, ":if")) {
|
||||
return _if(node2, exp, context2, component, componentProps, allProps);
|
||||
}
|
||||
if (exp = checkAndRemoveAttribute(node2, ":for")) {
|
||||
return _for(node2, exp, context2, component, componentProps, allProps);
|
||||
}
|
||||
if (exp = checkAndRemoveAttribute(node2, ":teleport")) {
|
||||
return _teleport(node2, exp, context2, component, componentProps, allProps);
|
||||
}
|
||||
if (exp = checkAndRemoveAttribute(node2, ":show")) {
|
||||
new ShowDirective({ element: node2, context: context2, expression: exp });
|
||||
}
|
||||
@ -1128,8 +1142,9 @@ function walk(node, context) {
|
||||
new ValueDirective({ element: node2, context: context2, expression: exp });
|
||||
}
|
||||
if (exp = checkAndRemoveAttribute(node2, ":html")) {
|
||||
const htmlExp = exp;
|
||||
context2.effect(() => {
|
||||
const result = evalGet(context2.scope, exp, node2);
|
||||
const result = evalGet(context2.scope, htmlExp, node2);
|
||||
if (result instanceof Element) {
|
||||
node2.replaceChildren();
|
||||
node2.append(result);
|
||||
@ -1139,8 +1154,9 @@ function walk(node, context) {
|
||||
});
|
||||
}
|
||||
if (exp = checkAndRemoveAttribute(node2, ":text")) {
|
||||
const textExp = exp;
|
||||
context2.effect(() => {
|
||||
node2.textContent = toDisplayString(evalGet(context2.scope, exp, node2));
|
||||
node2.textContent = toDisplayString(evalGet(context2.scope, textExp, node2));
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -1174,7 +1190,8 @@ function walk(node, context) {
|
||||
const templates = findTemplateNodes(node);
|
||||
return new Block({
|
||||
element: node,
|
||||
parentContext: context,
|
||||
app: current.componentBlock.context.app,
|
||||
// parentContext: context,
|
||||
component,
|
||||
replacementType: "replace",
|
||||
parentComponentBlock: current.componentBlock,
|
||||
@ -1247,17 +1264,22 @@ function flattenRefs(scope) {
|
||||
// src/demo.ts
|
||||
var main = {
|
||||
template: html`
|
||||
<div class="hero sans-serif f2" :scope="{ visible: true }">
|
||||
<div :show="visible">ON</div>
|
||||
<div :show="!visible">OFF</div>
|
||||
<button @click="() => visible = !visible" class="padding-x-1 padding-y-0_5 border-radius-1">Toggle</button>
|
||||
<div class="sans-serif margin-y-3 container" style="--column-gap: .5rem; --row-gap: .5rem;">
|
||||
<h1 class="f1 margin-bottom-1 color-5">Colors</h1>
|
||||
<grid columns="6" class="f6 white-space-nowrap">
|
||||
<div :for="variant in ['base', 'accent', 'red', 'rose', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'lavender', 'violet', 'purple', 'plum', 'fuchsia', 'pink', 'peach']" class="border-color-30 border-2px">
|
||||
<div :for="rank, index in ranks">
|
||||
<div .style:bind="bg(variant, rank, index)" class="padding-1">{{variant}}-{{rank}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</grid>
|
||||
</div>
|
||||
`,
|
||||
main() {
|
||||
const onClick = () => {
|
||||
console.log("ok");
|
||||
};
|
||||
return { onClick };
|
||||
const ranks = reactive(["5", "10", "20", "30", "40", "50", "60", "70", "80", "90"]);
|
||||
const basesReverse = computed(() => Array.from(ranks).reverse());
|
||||
const bg = (variant, rank, index) => ({ backgroundColor: `var(--${variant}-${rank})`, color: `var(--${variant}-${basesReverse.value[index]})` });
|
||||
return { ranks, bg };
|
||||
}
|
||||
};
|
||||
var app = new App2();
|
||||
|
||||
File diff suppressed because one or more lines are too long
1727
public/index.js
1727
public/index.js
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
142
src/demo.ts
142
src/demo.ts
@ -1,4 +1,5 @@
|
||||
import { App } from ".";
|
||||
import { computed } from "./reactivity/computed";
|
||||
import { reactive } from "./reactivity/reactive";
|
||||
import { ref } from "./reactivity/ref";
|
||||
import { html } from "./util";
|
||||
@ -169,11 +170,16 @@ import { html } from "./util";
|
||||
// Event directive
|
||||
// const counter = {
|
||||
// template: html`
|
||||
// <div>
|
||||
// <div :teleport="body" :if="style.color === 'gray'">true</div>
|
||||
// <div class="sans-serif">
|
||||
// <div :teleport="body">
|
||||
// <div :if="style.color === 'gray'">true</div>
|
||||
// </div>
|
||||
// <h3 {style:bind}>Count: {{count}}{{count >= 2 ? '!!!' : ''}}</h3>
|
||||
// <button @click="increment">+</button>
|
||||
// <button @click="decrement">-</button>
|
||||
// <button @click="increment" class="padding-x-1 padding-y-0_5">+</button>
|
||||
// <button @click="decrement" class="padding-x-1 padding-y-0_5">-</button>
|
||||
// <div :teleport="body">
|
||||
// <div :for="color in ['red', 'green', 'blue']">{{color}}</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// `,
|
||||
// main() {
|
||||
@ -194,12 +200,18 @@ import { html } from "./util";
|
||||
// app.mount(counter, "#app");
|
||||
|
||||
// ------------------------------------------------
|
||||
// Template
|
||||
// :if above :for
|
||||
// :if with :teleport
|
||||
// const main = {
|
||||
// template: html`
|
||||
// <div>
|
||||
// <div :if="bool">
|
||||
// <div :for="item in items">{{item}}</div>
|
||||
// <div :teleport="body">
|
||||
// <div :for="item in items">{{item}}</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div :if="bool">
|
||||
// <div :teleport="body">if bool teleported! {{items}}</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// `,
|
||||
@ -236,37 +248,37 @@ import { html } from "./util";
|
||||
|
||||
// ------------------------------------------------
|
||||
// Colors from css framework
|
||||
// const main = {
|
||||
// template: html`
|
||||
// <div class="sans-serif margin-y-3 container" style="--column-gap: .5rem; --row-gap: .5rem;">
|
||||
// <h1 class="f1 margin-bottom-1 color-5">Colors</h1>
|
||||
// <grid columns="6" class="f6 white-space-nowrap">
|
||||
// <div :for="variant in ['base', 'accent', 'red', 'rose', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'lavender', 'violet', 'purple', 'plum', 'fuchsia', 'pink', 'peach']" class="border-color-30 border-2px">
|
||||
// <div :for="rank, index in ranks">
|
||||
// <div .style:bind="bg(variant, rank, index)" class="padding-1">{{variant}}-{{rank}}</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </grid>
|
||||
// </div>
|
||||
// `,
|
||||
// main() {
|
||||
// const ranks = reactive(["5", "10", "20", "30", "40", "50", "60", "70", "80", "90"]);
|
||||
// const basesReverse = computed(() => Array.from(ranks).reverse());
|
||||
// const bg = (variant: string, rank: string, index: number) => ({ backgroundColor: `var(--${variant}-${rank})`, color: `var(--${variant}-${basesReverse.value[index]})` });
|
||||
const main = {
|
||||
template: html`
|
||||
<div class="sans-serif margin-y-3 container" style="--column-gap: .5rem; --row-gap: .5rem;">
|
||||
<h1 class="f1 margin-bottom-1 color-5">Colors</h1>
|
||||
<grid columns="6" class="f6 white-space-nowrap">
|
||||
<div :for="variant in ['base', 'accent', 'red', 'rose', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'lavender', 'violet', 'purple', 'plum', 'fuchsia', 'pink', 'peach']" class="border-color-30 border-2px">
|
||||
<div :for="rank, index in ranks">
|
||||
<div .style:bind="bg(variant, rank, index)" class="padding-1">{{variant}}-{{rank}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</grid>
|
||||
</div>
|
||||
`,
|
||||
main() {
|
||||
const ranks = reactive(["5", "10", "20", "30", "40", "50", "60", "70", "80", "90"]);
|
||||
const basesReverse = computed(() => Array.from(ranks).reverse());
|
||||
const bg = (variant: string, rank: string, index: number) => ({ backgroundColor: `var(--${variant}-${rank})`, color: `var(--${variant}-${basesReverse.value[index]})` });
|
||||
|
||||
// return { ranks, bg };
|
||||
// },
|
||||
// };
|
||||
return { ranks, bg };
|
||||
},
|
||||
};
|
||||
|
||||
// const app = new App();
|
||||
// app.mount(main, "#app");
|
||||
const app = new App();
|
||||
app.mount(main, "#app");
|
||||
|
||||
// ------------------------------------------------
|
||||
// :scope
|
||||
// const child = {
|
||||
// template: html`
|
||||
// <div>
|
||||
// hello from child, food: "{{food}}" (does not inherit)
|
||||
// I am child and I have food: "{{food}}" (does not inherit)
|
||||
// <div>
|
||||
// <slot />
|
||||
// </div>
|
||||
@ -277,16 +289,13 @@ import { html } from "./util";
|
||||
// return { food };
|
||||
// },
|
||||
// };
|
||||
//
|
||||
|
||||
// const main = {
|
||||
// template: html`
|
||||
// <div class="hero sans-serif f2" :scope="{ drink: '🍹' }">
|
||||
// <div :scope="{ food: '🍕' }">
|
||||
// <!-- <div> -->
|
||||
// <div>Scoped data: {{food}}</div>
|
||||
// <div>Scoped food: {{food}} and scoped drink: {{drink}}</div>
|
||||
// <child>Child slot, food: {{food}} {{drink}}</child>
|
||||
// <div :if="food === 'nothing'">No pizza 😢</div>
|
||||
// <div :else-if="food === '🍕'">Pizza!</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// `,
|
||||
@ -294,29 +303,58 @@ import { html } from "./util";
|
||||
// return { food: ref("nothing") };
|
||||
// },
|
||||
// };
|
||||
//
|
||||
|
||||
// const app = new App();
|
||||
// app.register("child", child);
|
||||
// app.mount(main, "#app");
|
||||
|
||||
// ------------------------------------------------
|
||||
// Practical :scope demo
|
||||
const main = {
|
||||
template: html`
|
||||
<div class="hero sans-serif f2" :scope="{ visible: true }">
|
||||
<div :show="visible">ON</div>
|
||||
<div :show="!visible">OFF</div>
|
||||
<button @click="() => visible = !visible" class="padding-x-1 padding-y-0_5 border-radius-1">Toggle</button>
|
||||
</div>
|
||||
`,
|
||||
main() {
|
||||
const onClick = () => {
|
||||
console.log("ok");
|
||||
};
|
||||
// const main = {
|
||||
// template: html`
|
||||
// <div class="hero sans-serif f2" :scope="{ visible: true }">
|
||||
// <div :show="visible">ON</div>
|
||||
// <div :show="!visible">OFF</div>
|
||||
// <button @click="() => visible = !visible" class="padding-x-1 padding-y-0_5 border-radius-1">Toggle</button>
|
||||
// </div>
|
||||
// `,
|
||||
// main() {
|
||||
// const onClick = () => {
|
||||
// console.log("ok");
|
||||
// };
|
||||
//
|
||||
// return { onClick };
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// const app = new App();
|
||||
// app.mount(main, "#app");
|
||||
|
||||
return { onClick };
|
||||
},
|
||||
};
|
||||
// --------
|
||||
// weird issue
|
||||
// const child = {
|
||||
// template: html`<div>child{{thing}}</div>`,
|
||||
// props: { thing: { default: 1 }},
|
||||
// main({ thing }) {
|
||||
// return { thing };
|
||||
// }
|
||||
// };
|
||||
|
||||
const app = new App();
|
||||
app.mount(main, "#app");
|
||||
// const counter = {
|
||||
// template: html`
|
||||
// <div class="sans-serif">
|
||||
// <div :teleport="body">
|
||||
// <div :for="color in colors">{{color}}</div>
|
||||
// </div>
|
||||
// <child :teleport="body" .thing="5" />
|
||||
// </div>
|
||||
// `,
|
||||
// main() {
|
||||
// const colors = reactive(["red", "green"]);
|
||||
// return { colors };
|
||||
// },
|
||||
// };
|
||||
|
||||
// const app = new App();
|
||||
// app.register("child", child);
|
||||
// app.mount(counter, "#app");
|
||||
|
||||
@ -88,7 +88,6 @@ export class AttributeDirective {
|
||||
this.element.classList.remove(c);
|
||||
});
|
||||
} else if (typeof value === "object" && this.extractedAttributeName === "style") {
|
||||
console.log("value is object", value)
|
||||
const next = Object.keys(value);
|
||||
const rm = Object.keys(this.previousStyles).filter((style) => !next.includes(style));
|
||||
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { Block, Context } from "..";
|
||||
import { nextTick } from "../util";
|
||||
import { Block, Component, Context } from "..";
|
||||
import { insertBefore } from "../util";
|
||||
|
||||
export function _teleport(el: Element, exp: string, ctx: Context) {
|
||||
const anchor = new Comment(":teleport");
|
||||
el.replaceWith(anchor);
|
||||
export function _teleport(el: Element, exp: string, ctx: Context, component?: Component, componentProps?: Record<string, any>, allProps?: Record<string, any>) {
|
||||
const anchor = new Comment(":teleport anchor");
|
||||
insertBefore(anchor, el);
|
||||
const observed = new Comment(":teleport");
|
||||
el.replaceWith(observed);
|
||||
console.log("Creating new block with allProps", component);
|
||||
|
||||
const target = document.querySelector(exp);
|
||||
if (!target) {
|
||||
@ -11,27 +14,40 @@ export function _teleport(el: Element, exp: string, ctx: Context) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
target.appendChild(el);
|
||||
// Prevent interpolations flashing before they can be compiled due to nextTick,
|
||||
// which is apparently required.
|
||||
// @ts-ignore
|
||||
const originalDisplay = el.style.display;
|
||||
// @ts-ignore
|
||||
el.style.display = "none";
|
||||
|
||||
const observer = new MutationObserver((mutationsList) => {
|
||||
mutationsList.forEach((mutation) => {
|
||||
mutation.removedNodes.forEach((removedNode) => {
|
||||
if (removedNode.contains(anchor)) {
|
||||
el.remove();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
let block: Block;
|
||||
|
||||
target.appendChild(el);
|
||||
|
||||
const observer = new MutationObserver((mutationsList) => {
|
||||
mutationsList.forEach((mutation) => {
|
||||
mutation.removedNodes.forEach((removedNode) => {
|
||||
if (removedNode.contains(observed)) {
|
||||
if (block.element) block.remove();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
// Walks the tree of this teleported element.
|
||||
new Block({
|
||||
element: el,
|
||||
parentContext: ctx,
|
||||
});
|
||||
// @ts-ignore
|
||||
el.style.display = originalDisplay;
|
||||
|
||||
block = new Block({
|
||||
element: el,
|
||||
parentContext: ctx,
|
||||
replacementType: "replace",
|
||||
component,
|
||||
componentProps,
|
||||
allProps,
|
||||
});
|
||||
|
||||
// Return the anchor so walk continues down the tree in the right order.
|
||||
|
||||
32
src/index.ts
32
src/index.ts
@ -11,7 +11,7 @@ import { isComputed } from "./reactivity/computed";
|
||||
import { effect as _effect } from "./reactivity/effect";
|
||||
import { reactive } from "./reactivity/reactive";
|
||||
import { isRef } from "./reactivity/ref";
|
||||
import { checkAndRemoveAttribute, componentHasPropByName, extractPropName, findSlotNodes, findTemplateNodes, isElement, isEventAttribute, isMirrorProp, isObject, isPropAttribute, isRegularProp, isSpreadProp, isText, Slot, stringToElement, Template, toDisplayString } from "./util";
|
||||
import { checkAndRemoveAttribute, componentHasPropByName, extractPropName, findSlotNodes, findTemplateNodes, insertBefore, isElement, isEventAttribute, isMirrorProp, isObject, isPropAttribute, isRegularProp, isSpreadProp, isText, nextTick, Slot, stringToElement, Template, toDisplayString } from "./util";
|
||||
|
||||
export * from "./plugins";
|
||||
export * from "./plugins/router";
|
||||
@ -64,7 +64,7 @@ export class App {
|
||||
const root = typeof target === "string" ? (document.querySelector(target) as HTMLElement) : target;
|
||||
const display = root.style.display;
|
||||
root.style.display = "none";
|
||||
this.root = this._mount(component, root, props);
|
||||
this._mount(component, root, props);
|
||||
root.style.display = display;
|
||||
return this.root;
|
||||
}
|
||||
@ -81,6 +81,7 @@ export class App {
|
||||
parentContext.scope.$isComputed = isComputed;
|
||||
|
||||
const block = new Block({
|
||||
app: this,
|
||||
element: target,
|
||||
parentContext,
|
||||
component,
|
||||
@ -116,8 +117,7 @@ interface CreateContextOptions {
|
||||
export function createContext({ parentContext, app }: CreateContextOptions): Context {
|
||||
const context: Context = {
|
||||
app: app ? app : parentContext && parentContext.app ? parentContext.app : null,
|
||||
// scope: parentContext ? parentContext.scope : reactive({}),
|
||||
scope: reactive({}),
|
||||
scope: parentContext ? parentContext.scope : reactive({}),
|
||||
blocks: [],
|
||||
effects: [],
|
||||
slots: [],
|
||||
@ -202,6 +202,7 @@ interface BlockOptions {
|
||||
componentProps?: Record<string, any>;
|
||||
allProps?: Record<string, any>;
|
||||
parentContext?: Context;
|
||||
app?: App;
|
||||
component?: Component;
|
||||
parentComponentBlock?: Block;
|
||||
templates?: Template[];
|
||||
@ -242,10 +243,11 @@ export class Block {
|
||||
|
||||
if (opts.isRoot) {
|
||||
this.context = opts.parentContext;
|
||||
opts.app.root = this;
|
||||
} else {
|
||||
this.parentContext = opts.parentContext ? opts.parentContext : createContext({});
|
||||
this.parentContext.blocks.push(this);
|
||||
this.context = createContext({ parentContext: opts.parentContext });
|
||||
this.context = createContext({ parentContext: opts.parentContext, app: opts.app });
|
||||
}
|
||||
|
||||
if (opts.component) {
|
||||
@ -374,7 +376,6 @@ export class Block {
|
||||
node = next;
|
||||
}
|
||||
} else {
|
||||
// this.element.parentNode!.removeChild(this.element);
|
||||
this.element.remove();
|
||||
}
|
||||
|
||||
@ -415,8 +416,8 @@ function walk(node: Node, context: Context) {
|
||||
|
||||
const handleDirectives = (node: Element, context: Context, component?: Component, componentProps?: Record<string, any>, allProps?: any[]) => {
|
||||
if (warnInvalidDirectives(node, [":if", ":for"])) return;
|
||||
if (warnInvalidDirectives(node, [":if", ":teleport"])) return;
|
||||
if (warnInvalidDirectives(node, [":for", ":teleport"])) return;
|
||||
if (warnInvalidDirectives(node, [":if", ":teleport"])) return;
|
||||
|
||||
// e.g. <div :scope="{ open: true }" />
|
||||
// In this case, the scope is merged into context.scope and will overwrite
|
||||
@ -429,15 +430,15 @@ function walk(node: Node, context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if ((exp = checkAndRemoveAttribute(node, ":teleport"))) {
|
||||
return _teleport(node, exp, context);
|
||||
}
|
||||
if ((exp = checkAndRemoveAttribute(node, ":if"))) {
|
||||
return _if(node, exp, context, component, componentProps, allProps);
|
||||
}
|
||||
if ((exp = checkAndRemoveAttribute(node, ":for"))) {
|
||||
return _for(node, exp, context, component, componentProps, allProps);
|
||||
}
|
||||
if ((exp = checkAndRemoveAttribute(node, ":teleport"))) {
|
||||
return _teleport(node, exp, context, component, componentProps, allProps);
|
||||
}
|
||||
if ((exp = checkAndRemoveAttribute(node, ":show"))) {
|
||||
new ShowDirective({ element: node, context, expression: exp });
|
||||
}
|
||||
@ -448,8 +449,10 @@ function walk(node: Node, context: Context) {
|
||||
new ValueDirective({ element: node, context, expression: exp });
|
||||
}
|
||||
if ((exp = checkAndRemoveAttribute(node, ":html"))) {
|
||||
const htmlExp = exp;
|
||||
|
||||
context.effect(() => {
|
||||
const result = evalGet(context.scope, exp, node);
|
||||
const result = evalGet(context.scope, htmlExp, node);
|
||||
if (result instanceof Element) {
|
||||
node.replaceChildren();
|
||||
node.append(result);
|
||||
@ -459,8 +462,10 @@ function walk(node: Node, context: Context) {
|
||||
});
|
||||
}
|
||||
if ((exp = checkAndRemoveAttribute(node, ":text"))) {
|
||||
const textExp = exp;
|
||||
|
||||
context.effect(() => {
|
||||
node.textContent = toDisplayString(evalGet(context.scope, exp, node));
|
||||
node.textContent = toDisplayString(evalGet(context.scope, textExp, node));
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -502,7 +507,8 @@ function walk(node: Node, context: Context) {
|
||||
|
||||
return new Block({
|
||||
element: node,
|
||||
parentContext: context,
|
||||
app: current.componentBlock.context.app,
|
||||
// parentContext: context,
|
||||
component,
|
||||
replacementType: "replace",
|
||||
parentComponentBlock: current.componentBlock,
|
||||
|
||||
@ -150,6 +150,10 @@ export function insertAfter(newNode: Node, existingNode: Node) {
|
||||
}
|
||||
}
|
||||
|
||||
export function insertBefore(newNode: Node, existingNode: Node) {
|
||||
existingNode.parentNode?.insertBefore(newNode, existingNode);
|
||||
}
|
||||
|
||||
export function isPropAttribute(attrName: string) {
|
||||
if (attrName.startsWith(".")) {
|
||||
return true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user