mirror of
https://github.com/MercuryWorkshop/dreamlandjs.git
synced 2025-05-16 15:40:01 -04:00
chore: setup and run prettier
This commit is contained in:
parent
ffe095557e
commit
7a131a3e2e
17 changed files with 1418 additions and 1512 deletions
123
DreamlandJS.d.ts
vendored
123
DreamlandJS.d.ts
vendored
|
@ -1,84 +1,97 @@
|
||||||
declare namespace JSX {
|
declare namespace JSX {
|
||||||
export type IntrinsicElements = {
|
export type IntrinsicElements = {
|
||||||
[index: string]: any
|
[index: string]: any
|
||||||
};
|
}
|
||||||
type ElementType = Fragment | string | Component<any, any>;
|
type ElementType = Fragment | string | Component<any, any>
|
||||||
type Element = DLElement<any>;
|
type Element = DLElement<any>
|
||||||
|
|
||||||
interface ElementAttributesProperty {
|
interface ElementAttributesProperty {
|
||||||
props: {};
|
props: {}
|
||||||
}
|
}
|
||||||
interface ElementChildrenAttribute {
|
interface ElementChildrenAttribute {
|
||||||
children: {};
|
children: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare function h(
|
declare function h(
|
||||||
type: string,
|
type: string,
|
||||||
props?: { [index: string]: any } | null,
|
props?: { [index: string]: any } | null,
|
||||||
...children: (HTMLElement | string)[]
|
...children: (HTMLElement | string)[]
|
||||||
): Node;
|
): Node
|
||||||
declare function $if(condition: DLPointer<any> | any, then?: Element, otherwise?: Element): HTMLElement;
|
declare function $if(
|
||||||
|
condition: DLPointer<any> | any,
|
||||||
|
then?: Element,
|
||||||
|
otherwise?: Element
|
||||||
|
): HTMLElement
|
||||||
|
|
||||||
type DLPointer<T> = { readonly __symbol: unique symbol, readonly __signature: T };
|
type DLPointer<T> = {
|
||||||
|
readonly __symbol: unique symbol
|
||||||
|
readonly __signature: T
|
||||||
|
}
|
||||||
|
|
||||||
declare function use<T>(sink: T, mapping?: (arg: T) => any): DLPointer<T>;
|
declare function use<T>(sink: T, mapping?: (arg: T) => any): DLPointer<T>
|
||||||
declare function useValue<T>(trap: DLPointer<T>): T;
|
declare function useValue<T>(trap: DLPointer<T>): T
|
||||||
|
|
||||||
type Stateful<T> = T & { readonly symbol: unique symbol };
|
type Stateful<T> = T & { readonly symbol: unique symbol }
|
||||||
|
|
||||||
|
declare function stateful<T>(target: T): Stateful<T>
|
||||||
|
declare function $state<T>(target: T): Stateful<T>
|
||||||
|
declare function $store<T>(
|
||||||
|
target: T,
|
||||||
|
ident: string,
|
||||||
|
backing: 'localstorage'
|
||||||
|
): Stateful<T>
|
||||||
|
|
||||||
declare function stateful<T>(target: T): Stateful<T>;
|
declare function handle<T>(
|
||||||
declare function $state<T>(target: T): Stateful<T>;
|
references: DLPointer<T>,
|
||||||
declare function $store<T>(target: T, ident: string, backing: "localstorage"): Stateful<T>;
|
callback: (value: T) => void
|
||||||
|
): void
|
||||||
|
|
||||||
declare function handle<T>(references: DLPointer<T>, callback: (value: T) => void): void;
|
declare function css(strings: TemplateStringsArray, ...values: any): string
|
||||||
|
declare function rule(strings: TemplateStringsArray, ...values: any): string
|
||||||
|
declare var styled: { new: typeof css; rule: typeof rule }
|
||||||
|
|
||||||
declare function css(strings: TemplateStringsArray, ...values: any): string;
|
type DLCSS = string
|
||||||
declare function rule(strings: TemplateStringsArray, ...values: any): string;
|
|
||||||
declare var styled: { new: typeof css, rule: typeof rule };
|
|
||||||
|
|
||||||
type DLCSS = string;
|
declare var $el: HTMLElement
|
||||||
|
|
||||||
declare var $el: HTMLElement;
|
type Fragment = { readonly fragment: unique symbol }
|
||||||
|
declare var Fragment: Fragment
|
||||||
type Fragment = { readonly fragment: unique symbol };
|
|
||||||
declare var Fragment: Fragment;
|
|
||||||
|
|
||||||
interface Element {
|
interface Element {
|
||||||
$: OuterComponentTypes & { [index: string | symbol]: any }
|
$: OuterComponentTypes & { [index: string | symbol]: any }
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DLElement<T> extends HTMLElement {
|
interface DLElement<T> extends HTMLElement {
|
||||||
$: T & OuterComponentTypes
|
$: T & OuterComponentTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
type ComponentElement<T extends (...args: any) => any> = ReturnType<T>;
|
type ComponentElement<T extends (...args: any) => any> = ReturnType<T>
|
||||||
|
|
||||||
type OuterComponentTypes = {
|
type OuterComponentTypes = {
|
||||||
root: Element,
|
root: Element
|
||||||
children: Element[],
|
children: Element[]
|
||||||
}
|
}
|
||||||
type InnerComponentTypes = {
|
type InnerComponentTypes = {
|
||||||
css: DLCSS,
|
css: DLCSS
|
||||||
mount?: () => void,
|
mount?: () => void
|
||||||
}
|
}
|
||||||
type ComponentTypes = OuterComponentTypes & InnerComponentTypes;
|
type ComponentTypes = OuterComponentTypes & InnerComponentTypes
|
||||||
|
|
||||||
type ArrayOrSingular<T extends []> = T | T[keyof T];
|
type ArrayOrSingular<T extends []> = T | T[keyof T]
|
||||||
|
|
||||||
type Component<Public, Private, Constructed extends string | symbol | number = never> =
|
type Component<
|
||||||
(
|
Public,
|
||||||
(
|
Private,
|
||||||
this: Public & Private & ComponentTypes,
|
Constructed extends string | symbol | number = never,
|
||||||
props: (
|
> = (
|
||||||
[Constructed] extends [never] ? Public : Omit<Public, Constructed>
|
this: Public & Private & ComponentTypes,
|
||||||
) &
|
props: ([Constructed] extends [never]
|
||||||
{
|
? Public
|
||||||
children?: ArrayOrSingular<Private extends { children: any } ? Private["children"] : never>
|
: Omit<Public, Constructed>) & {
|
||||||
|
children?: ArrayOrSingular<
|
||||||
|
Private extends { children: any } ? Private['children'] : never
|
||||||
|
>
|
||||||
[index: `${'bind:'}${string}`]: any
|
[index: `${'bind:'}${string}`]: any
|
||||||
},
|
}
|
||||||
) => DLElement<Public>
|
) => DLElement<Public>
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
135
README.md
135
README.md
|
@ -1,83 +1,89 @@
|
||||||
## What is Dreamland?
|
## What is Dreamland?
|
||||||
|
|
||||||
dreamland.js is a reactive JSX-inspired rendering library with **no virtual dom** and **no build step**
|
dreamland.js is a reactive JSX-inspired rendering library with **no virtual dom** and **no build step**
|
||||||
|
|
||||||
## Why Dreamland?
|
## Why Dreamland?
|
||||||
|
|
||||||
We've found frameworks such as React to be cumbersome, with more than just a few footguns. Dreamland can get you fast results with brutal simplicity. See the [documentation](https://dreamland.js.org) for more information.
|
We've found frameworks such as React to be cumbersome, with more than just a few footguns. Dreamland can get you fast results with brutal simplicity. See the [documentation](https://dreamland.js.org) for more information.
|
||||||
|
|
||||||
## What does it look like?
|
## What does it look like?
|
||||||
|
|
||||||
Here's a simple counter app
|
Here's a simple counter app
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
function App() {
|
function App() {
|
||||||
this.counter = 0;
|
this.counter = 0
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button on:click={() => this.counter++}>Click me!</button>
|
<button on:click={() => this.counter++}>Click me!</button>
|
||||||
<p>
|
<p>{use(this.counter)}</p>
|
||||||
{use(this.counter)}
|
</div>
|
||||||
</p>
|
)
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener('load', () => {
|
||||||
document.body.appendChild(<App/>);
|
document.body.appendChild(<App />)
|
||||||
});
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Compare that to the equivalent code in react:
|
Compare that to the equivalent code in react:
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { React, useState } from 'react'
|
import { React, useState } from 'react'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [counter, setCounter] = useState(0);
|
const [counter, setCounter] = useState(0)
|
||||||
|
|
||||||
const increase = () => {
|
const increase = () => {
|
||||||
setCounter(count => count + 1);
|
setCounter((count) => count + 1)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button onClick={increase}>Click me!</button>
|
<button onClick={increase}>Click me!</button>
|
||||||
<p>
|
<p>Value: {counter}</p>
|
||||||
Value: {counter}
|
</div>
|
||||||
</p>
|
)
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById("root")
|
document.getElementById('root')
|
||||||
);
|
)
|
||||||
```
|
```
|
||||||
The idea of dreamland is to get some of the convience of big framworks at a ridiculously tiny size (~3kb, smaller than preact) with less hurdles.
|
|
||||||
|
The idea of dreamland is to get some of the convience of big framworks at a ridiculously tiny size (~3kb, smaller than preact) with less hurdles.
|
||||||
|
|
||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
||||||
Dreamland can be integrated into plain-javascript applications gradually and seamlessly. See the [Wiki](https://github.com/MercuryWorkshop/dreamlandjs/wiki) for learning the concepts that dreamland uses.
|
Dreamland can be integrated into plain-javascript applications gradually and seamlessly. See the [Wiki](https://github.com/MercuryWorkshop/dreamlandjs/wiki) for learning the concepts that dreamland uses.
|
||||||
|
|
||||||
## Plain JS
|
## Plain JS
|
||||||
|
|
||||||
In your HTML file, add `<script src="https://unpkg.com/dreamland"></script>` somewhere. This unlocks the html builder allowing you to start writing dreamland code, such as the example shown below
|
In your HTML file, add `<script src="https://unpkg.com/dreamland"></script>` somewhere. This unlocks the html builder allowing you to start writing dreamland code, such as the example shown below
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
function App() {
|
function App() {
|
||||||
this.counter = 0;
|
this.counter = 0
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
<button on:click=${() => this.counter++}>Click me!</button>
|
<button on:click=${() => this.counter++}>Click me!</button>
|
||||||
<p>
|
<p>${use(this.counter)}</p>
|
||||||
${use(this.counter)}
|
</div>
|
||||||
</p>
|
`
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener('load', () => {
|
||||||
document.body.appendChild(h(App));
|
document.body.appendChild(h(App))
|
||||||
});
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
## Typescript + Bundler (vite, rollup, webpack, esbuild, etc)
|
## Typescript + Bundler (vite, rollup, webpack, esbuild, etc)
|
||||||
|
|
||||||
First install dreamland (`npm install dreamland`), then add this to the compileroptions of your `tsconfig.json` to setup JSX.
|
First install dreamland (`npm install dreamland`), then add this to the compileroptions of your `tsconfig.json` to setup JSX.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"jsx":"react",
|
"jsx":"react",
|
||||||
"jsxFactory":"h",
|
"jsxFactory":"h",
|
||||||
|
@ -89,27 +95,28 @@ In the entry point of the app, add the line `import "dreamland/dev"` into at lea
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// typescript syntax for defining components
|
// typescript syntax for defining components
|
||||||
const App: Component<{
|
const App: Component<
|
||||||
// component properties. if you had a component that took a property like `<Button text="..." /> you would use a type like the one in the following line
|
{
|
||||||
// text: string
|
// component properties. if you had a component that took a property like `<Button text="..." /> you would use a type like the one in the following line
|
||||||
},{
|
// text: string
|
||||||
// types for internal state
|
},
|
||||||
counter: number
|
{
|
||||||
}> = function() {
|
// types for internal state
|
||||||
this.counter = 0;
|
counter: number
|
||||||
return (
|
}
|
||||||
<div>
|
> = function () {
|
||||||
<button on:click={() => this.counter++}>Click me!</button>
|
this.counter = 0
|
||||||
<p>
|
return (
|
||||||
{use(this.counter)}
|
<div>
|
||||||
</p>
|
<button on:click={() => this.counter++}>Click me!</button>
|
||||||
</div>
|
<p>{use(this.counter)}</p>
|
||||||
);
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener('load', () => {
|
||||||
document.body.appendChild(<App/>);
|
document.body.appendChild(<App />)
|
||||||
});
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [documentation](https://dreamland.js.org) for more information.
|
See the [documentation](https://dreamland.js.org) for more information.
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Dreamland examples</title>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" href="index.css" />
|
||||||
|
<script src="../index.js"></script>
|
||||||
|
|
||||||
<head>
|
<script src="lib/index.js"></script>
|
||||||
<title>Dreamland examples</title>
|
</head>
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="stylesheet" href="index.css" />
|
|
||||||
<script src="../index.js"></script>
|
|
||||||
|
|
||||||
<script src="lib/index.js"></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
<body></body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
html {
|
html {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
background-color: #191724;
|
background-color: #191724;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
background-color: #1f1d2e;
|
background-color: #1f1d2e;
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
padding:2em;
|
padding: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
p,h1 {
|
p,
|
||||||
font-family: "serif";
|
h1 {
|
||||||
color: #e0def4;
|
font-family: 'serif';
|
||||||
|
color: #e0def4;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,39 @@
|
||||||
function Counter() {
|
function Counter() {
|
||||||
this.css = css`
|
this.css = css`
|
||||||
self {
|
self {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
width: 10em;
|
width: 10em;
|
||||||
height:5em;
|
height: 5em;
|
||||||
background-color: #f6c177;
|
background-color: #f6c177;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
|
|
||||||
this.counter ??= 0;
|
this.counter ??= 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h1>Counter</h1>
|
<h1>Counter</h1>
|
||||||
<p>
|
<p>Value: {use(this.counter)}</p>
|
||||||
Value: {use(this.counter)}
|
<button on:click={() => this.counter++}>Click me!</button>
|
||||||
</p>
|
<p>
|
||||||
<button on:click={() => this.counter++} >Click me!</button>
|
is {use(this.counter)} odd?{' '}
|
||||||
<p>
|
{use(this.counter, (p) => p % 2 == 1)}
|
||||||
is {use(this.counter)} odd? {use(this.counter, p => p % 2 == 1)}
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// function ToDoList() {
|
// function ToDoList() {
|
||||||
|
@ -120,14 +119,20 @@ function Counter() {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener('load', () => {
|
||||||
document.body.appendChild(<Counter />);
|
document.body.appendChild(<Counter />)
|
||||||
});
|
})
|
||||||
|
|
||||||
let a = stateful({ b: stateful({ c: stateful({ d: 0 }) }), array: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] }) as any;
|
let a = stateful({
|
||||||
let r = use(a.array[a.b.c.d][a.b.c.d]);
|
b: stateful({ c: stateful({ d: 0 }) }),
|
||||||
|
array: [
|
||||||
|
[1, 2, 3],
|
||||||
|
[4, 5, 6],
|
||||||
|
[7, 8, 9],
|
||||||
|
],
|
||||||
|
}) as any
|
||||||
|
let r = use(a.array[a.b.c.d][a.b.c.d])
|
||||||
|
|
||||||
|
handle(r, (v) => {
|
||||||
handle(r, v => {
|
console.log(v)
|
||||||
console.log(v);
|
})
|
||||||
});
|
|
||||||
|
|
101
package.json
101
package.json
|
@ -1,55 +1,56 @@
|
||||||
{
|
{
|
||||||
"name": "dreamland",
|
"name": "dreamland",
|
||||||
"version": "0.0.3",
|
"version": "0.0.4",
|
||||||
"description": "A utilitarian HTML rendering library",
|
"description": "A utilitarian HTML rendering library",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c && prettier . --check",
|
||||||
"watch": "rollup -cw"
|
"watch": "rollup -cw",
|
||||||
},
|
"publish": "npm publish --access public"
|
||||||
"keywords": [
|
|
||||||
"html",
|
|
||||||
"jsx",
|
|
||||||
"framework",
|
|
||||||
"dreamlandjs",
|
|
||||||
"dreamland"
|
|
||||||
],
|
|
||||||
"author": "MercuryWorkshop",
|
|
||||||
"repository": "https://github.com/MercuryWorkshop/dreamlandjs",
|
|
||||||
"license": "MIT",
|
|
||||||
"browser": "./dist/dev/index.js",
|
|
||||||
"types": "./DreamlandJS.d.ts",
|
|
||||||
"node": "./dist/dev/index.js",
|
|
||||||
"exports": {
|
|
||||||
"./dev": {
|
|
||||||
"default": "./dist/dev/index.js",
|
|
||||||
"types": "./DreamlandJS.d.ts"
|
|
||||||
},
|
},
|
||||||
"./js": {
|
"keywords": [
|
||||||
"default": "./dist/js.js",
|
"html",
|
||||||
"types": "./DreamlandJS.d.ts"
|
"jsx",
|
||||||
|
"framework",
|
||||||
|
"dreamlandjs",
|
||||||
|
"dreamland"
|
||||||
|
],
|
||||||
|
"author": "MercuryWorkshop",
|
||||||
|
"repository": "https://github.com/MercuryWorkshop/dreamlandjs",
|
||||||
|
"license": "MIT",
|
||||||
|
"browser": "./dist/dev/index.js",
|
||||||
|
"types": "./DreamlandJS.d.ts",
|
||||||
|
"node": "./dist/dev/index.js",
|
||||||
|
"exports": {
|
||||||
|
"./dev": {
|
||||||
|
"default": "./dist/dev/index.js",
|
||||||
|
"types": "./DreamlandJS.d.ts"
|
||||||
|
},
|
||||||
|
"./js": {
|
||||||
|
"default": "./dist/js.js",
|
||||||
|
"types": "./DreamlandJS.d.ts"
|
||||||
|
},
|
||||||
|
"./css": {
|
||||||
|
"default": "./dist/css.js",
|
||||||
|
"types": "./DreamlandJS.d.ts"
|
||||||
|
},
|
||||||
|
"./html": {
|
||||||
|
"default": "./dist/html.js",
|
||||||
|
"types": "./DreamlandJS.d.ts"
|
||||||
|
},
|
||||||
|
"./store": {
|
||||||
|
"default": "./dist/store.js",
|
||||||
|
"types": "./DreamlandJS.d.ts"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"./css": {
|
"files": [
|
||||||
"default": "./dist/css.js",
|
"dist",
|
||||||
"types": "./DreamlandJS.d.ts"
|
"DreamlandJS.d.ts"
|
||||||
},
|
],
|
||||||
"./html": {
|
"devDependencies": {
|
||||||
"default": "./dist/html.js",
|
"@rollup/plugin-strip": "^3.0.4",
|
||||||
"types": "./DreamlandJS.d.ts"
|
"prettier": "^3.2.5",
|
||||||
},
|
"rollup": "^4.12.1",
|
||||||
"./store": {
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"default": "./dist/store.js",
|
"typescript": "^5.3.3"
|
||||||
"types": "./DreamlandJS.d.ts"
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist",
|
|
||||||
"DreamlandJS.d.ts"
|
|
||||||
],
|
|
||||||
|
|
||||||
"devDependencies": {
|
|
||||||
"rollup": "^4.12.1",
|
|
||||||
"@rollup/plugin-strip": "^3.0.4",
|
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
|
||||||
"typescript": "^5.3.3"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
1283
pnpm-lock.yaml
generated
1283
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
11
prettier.config.js
Normal file
11
prettier.config.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// prettier.config.js, .prettierrc.js, prettier.config.cjs, or .prettierrc.cjs
|
||||||
|
|
||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
const config = {
|
||||||
|
trailingComma: 'es5',
|
||||||
|
tabWidth: 4,
|
||||||
|
semi: false,
|
||||||
|
singleQuote: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = config
|
|
@ -1,74 +1,74 @@
|
||||||
import strip from "@rollup/plugin-strip";
|
import strip from '@rollup/plugin-strip'
|
||||||
import { terser } from "rollup-plugin-terser";
|
import { terser } from 'rollup-plugin-terser'
|
||||||
|
|
||||||
const prodPlugins = [
|
const prodPlugins = [
|
||||||
strip({
|
strip({
|
||||||
functions: ["console.log", "assert.*", "panic", "log"],
|
functions: ['console.log', 'assert.*', 'panic', 'log'],
|
||||||
labels: ["dev"]
|
labels: ['dev'],
|
||||||
}),
|
}),
|
||||||
terser({
|
terser({
|
||||||
mangle: {
|
mangle: {
|
||||||
toplevel: true
|
toplevel: true,
|
||||||
},
|
},
|
||||||
compress: {
|
compress: {
|
||||||
unused: true,
|
unused: true,
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
toplevel: true
|
toplevel: true,
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
comments: false
|
comments: false,
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
const devPlugins = [];
|
const devPlugins = []
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
input: "src/dev.js",
|
input: 'src/dev.js',
|
||||||
output: {
|
output: {
|
||||||
file: "dist/dev/index.js",
|
file: 'dist/dev/index.js',
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
plugins: devPlugins,
|
||||||
},
|
},
|
||||||
plugins: devPlugins
|
{
|
||||||
},
|
input: 'src/js.js',
|
||||||
{
|
output: {
|
||||||
input: "src/js.js",
|
file: 'dist/js.js',
|
||||||
output: {
|
strict: false,
|
||||||
file: "dist/js.js",
|
format: 'cjs',
|
||||||
strict: false,
|
sourcemap: true,
|
||||||
format: 'cjs',
|
},
|
||||||
sourcemap: true,
|
plugins: prodPlugins,
|
||||||
},
|
},
|
||||||
plugins: prodPlugins
|
{
|
||||||
},
|
input: 'src/css.js',
|
||||||
{
|
output: {
|
||||||
input: "src/css.js",
|
file: 'dist/css.js',
|
||||||
output: {
|
strict: false,
|
||||||
file: "dist/css.js",
|
format: 'cjs',
|
||||||
strict: false,
|
sourcemap: true,
|
||||||
format: 'cjs',
|
},
|
||||||
sourcemap: true,
|
plugins: prodPlugins,
|
||||||
},
|
},
|
||||||
plugins: prodPlugins
|
{
|
||||||
},
|
input: 'src/html.js',
|
||||||
{
|
output: {
|
||||||
input: "src/html.js",
|
file: 'dist/html.js',
|
||||||
output: {
|
strict: false,
|
||||||
file: "dist/html.js",
|
format: 'cjs',
|
||||||
strict: false,
|
sourcemap: true,
|
||||||
format: 'cjs',
|
},
|
||||||
sourcemap: true,
|
plugins: prodPlugins,
|
||||||
},
|
},
|
||||||
plugins: prodPlugins
|
{
|
||||||
},
|
input: 'src/store.js',
|
||||||
{
|
output: {
|
||||||
input: "src/store.js",
|
file: 'dist/store.js',
|
||||||
output: {
|
strict: false,
|
||||||
file: "dist/store.js",
|
format: 'cjs',
|
||||||
strict: false,
|
sourcemap: true,
|
||||||
format: 'cjs',
|
},
|
||||||
sourcemap: true,
|
plugins: prodPlugins,
|
||||||
},
|
},
|
||||||
plugins: prodPlugins
|
]
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
|
|
||||||
class DreamlandError extends Error {
|
class DreamlandError extends Error {
|
||||||
constructor(message) {
|
constructor(message) {
|
||||||
super("[dreamland-js/dev] " + message);
|
super('[dreamland-js/dev] ' + message)
|
||||||
this.name = "DreamlandDevError";
|
this.name = 'DreamlandDevError'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function log(message) {
|
export function log(message) {
|
||||||
console.log("[dreamland-js/dev] " + message);
|
console.log('[dreamland-js/dev] ' + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function panic(message) {
|
export function panic(message) {
|
||||||
throw new DreamlandError("fatal: " + message);
|
throw new DreamlandError('fatal: ' + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assert(condition, message) {
|
export function assert(condition, message) {
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
panic(message);
|
panic(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dev: assert.eq = (a, b) => {
|
dev: assert.eq = (a, b) => {
|
||||||
if (a != b) panic("Assertion failed: " + a + " != " + b);
|
if (a != b) panic('Assertion failed: ' + a + ' != ' + b)
|
||||||
};
|
}
|
||||||
dev: assert.neq = (a, b) => {
|
dev: assert.neq = (a, b) => {
|
||||||
if (a == b) panic("Assertion failed: " + a + " == " + b);
|
if (a == b) panic('Assertion failed: ' + a + ' == ' + b)
|
||||||
};
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export const VERSION = "0.0.2-beta";
|
export const VERSION = '0.0.2-beta'
|
||||||
|
|
120
src/css.js
120
src/css.js
|
@ -1,84 +1,76 @@
|
||||||
Object.assign(window, { css, rule, styled: { new: css, rule: rule } });
|
Object.assign(window, { css, rule, styled: { new: css, rule: rule } })
|
||||||
const cssmap = {};
|
const cssmap = {}
|
||||||
function scopify_css(uid, css) {
|
function scopify_css(uid, css) {
|
||||||
const virtualDoc = document.implementation.createHTMLDocument("");
|
const virtualDoc = document.implementation.createHTMLDocument('')
|
||||||
const virtualStyleElement = document.createElement("style");
|
const virtualStyleElement = document.createElement('style')
|
||||||
virtualDoc.body.appendChild(virtualStyleElement);
|
virtualDoc.body.appendChild(virtualStyleElement)
|
||||||
|
|
||||||
let cssParsed = "";
|
let cssParsed = ''
|
||||||
|
|
||||||
virtualStyleElement.textContent = css;
|
virtualStyleElement.textContent = css
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
for (const rule of virtualStyleElement.sheet.cssRules) {
|
for (const rule of virtualStyleElement.sheet.cssRules) {
|
||||||
rule.selectorText = rule.selectorText.includes("self")
|
rule.selectorText = rule.selectorText.includes('self')
|
||||||
? `.${uid}.self${rule.selectorText.replace("self", "")}`
|
? `.${uid}.self${rule.selectorText.replace('self', '')}`
|
||||||
: `.${uid} ${rule.selectorText}`;
|
: `.${uid} ${rule.selectorText}`
|
||||||
cssParsed += `${rule.cssText}\n`;
|
cssParsed += `${rule.cssText}\n`
|
||||||
}
|
}
|
||||||
|
|
||||||
return cssParsed;
|
return cssParsed
|
||||||
}
|
}
|
||||||
function tagcss(strings, values, isblock) {
|
function tagcss(strings, values, isblock) {
|
||||||
let cached = cssmap[strings[0]];
|
let cached = cssmap[strings[0]]
|
||||||
let cachable = strings.length == 1;
|
let cachable = strings.length == 1
|
||||||
if (cachable && cached) return cached;
|
if (cachable && cached) return cached
|
||||||
const uid = `dl${Array(5)
|
const uid = `dl${Array(5)
|
||||||
.fill(0)
|
.fill(0)
|
||||||
.map(() => {
|
.map(() => {
|
||||||
return Math.floor(Math.random() * 36).toString(36);
|
return Math.floor(Math.random() * 36).toString(36)
|
||||||
})
|
})
|
||||||
.join("")}`;
|
.join('')}`
|
||||||
|
|
||||||
const styleElement = document.createElement("style");
|
const styleElement = document.createElement('style')
|
||||||
|
|
||||||
document.head.appendChild(styleElement);
|
document.head.appendChild(styleElement)
|
||||||
|
|
||||||
const flattened_template = [];
|
const flattened_template = []
|
||||||
for (const i in strings) {
|
for (const i in strings) {
|
||||||
flattened_template.push(strings[i]);
|
flattened_template.push(strings[i])
|
||||||
if (values[i]) {
|
if (values[i]) {
|
||||||
const prop = values[i];
|
const prop = values[i]
|
||||||
|
|
||||||
if (isDLPtr(prop)) {
|
if (isDLPtr(prop)) {
|
||||||
const current_i = flattened_template.length;
|
const current_i = flattened_template.length
|
||||||
let oldparsed;
|
let oldparsed
|
||||||
handle(prop, (val) => {
|
handle(prop, (val) => {
|
||||||
flattened_template[current_i] = String(val);
|
flattened_template[current_i] = String(val)
|
||||||
let parsed = flattened_template.join("");
|
let parsed = flattened_template.join('')
|
||||||
if (parsed != oldparsed)
|
if (parsed != oldparsed)
|
||||||
if (isblock)
|
if (isblock)
|
||||||
styleElement.textContent = scopify_css(
|
styleElement.textContent = scopify_css(uid, parsed)
|
||||||
uid,
|
else styleElement.textContent = `.${uid} { ${parsed}; }`
|
||||||
parsed,
|
oldparsed = parsed
|
||||||
);
|
})
|
||||||
else
|
} else {
|
||||||
styleElement.textContent = `.${uid} { ${parsed}; }`
|
flattened_template.push(String(prop))
|
||||||
oldparsed = parsed;
|
}
|
||||||
});
|
}
|
||||||
} else {
|
|
||||||
flattened_template.push(String(prop));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isblock) {
|
if (isblock) {
|
||||||
styleElement.textContent = scopify_css(
|
styleElement.textContent = scopify_css(uid, flattened_template.join(''))
|
||||||
uid,
|
} else {
|
||||||
flattened_template.join(""),
|
styleElement.textContent = `.${uid} { ${flattened_template.join('')}; }`
|
||||||
);
|
}
|
||||||
} else {
|
|
||||||
styleElement.textContent = `.${uid} { ${flattened_template.join("")}; }`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cachable) cssmap[strings[0]] = uid;
|
if (cachable) cssmap[strings[0]] = uid
|
||||||
|
|
||||||
return uid;
|
return uid
|
||||||
}
|
}
|
||||||
function rule(strings, ...values) {
|
function rule(strings, ...values) {
|
||||||
return tagcss(strings, values, false)
|
return tagcss(strings, values, false)
|
||||||
}
|
}
|
||||||
function css(strings, ...values) {
|
function css(strings, ...values) {
|
||||||
return tagcss(strings, values, true);
|
return tagcss(strings, values, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
src/dev.js
18
src/dev.js
|
@ -1,10 +1,12 @@
|
||||||
import { log } from "./asserts"
|
import { log } from './asserts'
|
||||||
import { VERSION } from "./consts"
|
import { VERSION } from './consts'
|
||||||
|
|
||||||
import "./js"
|
import './js'
|
||||||
import "./css"
|
import './css'
|
||||||
import "./html"
|
import './html'
|
||||||
import "./store"
|
import './store'
|
||||||
|
|
||||||
log("Version: " + VERSION)
|
log('Version: ' + VERSION)
|
||||||
console.warn("This is a DEVELOPER build of dreamland.js. It is not suitable for production use.")
|
console.warn(
|
||||||
|
'This is a DEVELOPER build of dreamland.js. It is not suitable for production use.'
|
||||||
|
)
|
||||||
|
|
115
src/html.js
115
src/html.js
|
@ -1,65 +1,66 @@
|
||||||
Object.assign(window, { html });
|
Object.assign(window, { html })
|
||||||
function html(strings, ...values) {
|
function html(strings, ...values) {
|
||||||
let flattened = "";
|
let flattened = ''
|
||||||
let markers = {};
|
let markers = {}
|
||||||
for (const i in strings) {
|
for (const i in strings) {
|
||||||
let string = strings[i];
|
let string = strings[i]
|
||||||
let value = values[i];
|
let value = values[i]
|
||||||
|
|
||||||
flattened += string;
|
flattened += string
|
||||||
if (i < values.length) {
|
if (i < values.length) {
|
||||||
let dupe = Object.values(markers).findIndex(v => v == value);
|
let dupe = Object.values(markers).findIndex((v) => v == value)
|
||||||
if (dupe !== -1) {
|
if (dupe !== -1) {
|
||||||
flattened += Object.keys(markers)[dupe];
|
flattened += Object.keys(markers)[dupe]
|
||||||
} else {
|
} else {
|
||||||
let marker = "m" + Array(16).fill(0).map(() => Math.floor(Math.random() * 16).toString(16)).join("");
|
let marker =
|
||||||
markers[marker] = value;
|
'm' +
|
||||||
flattened += marker;
|
Array(16)
|
||||||
}
|
.fill(0)
|
||||||
|
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||||
|
.join('')
|
||||||
|
markers[marker] = value
|
||||||
|
flattened += marker
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
let dom = new DOMParser().parseFromString(flattened, 'text/html')
|
||||||
let dom = new DOMParser().parseFromString(flattened, "text/html");
|
if (dom.body.children.length !== 1)
|
||||||
if (dom.body.children.length !== 1)
|
throw 'html builder needs exactly one child'
|
||||||
throw "html builder needs exactly one child";
|
|
||||||
|
|
||||||
function wraph(elm) {
|
function wraph(elm) {
|
||||||
let nodename = elm.nodeName.toLowerCase();
|
let nodename = elm.nodeName.toLowerCase()
|
||||||
if (nodename === "#text")
|
if (nodename === '#text') return elm.textContent
|
||||||
return elm.textContent;
|
if (nodename in markers) nodename = markers[nodename]
|
||||||
if (nodename in markers)
|
|
||||||
nodename = markers[nodename];
|
|
||||||
|
|
||||||
let children = [...elm.childNodes].map(wraph);
|
let children = [...elm.childNodes].map(wraph)
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
let text = children[i];
|
let text = children[i]
|
||||||
if (typeof text !== "string") continue;
|
if (typeof text !== 'string') continue
|
||||||
for (const [marker, value] of Object.entries(markers)) {
|
for (const [marker, value] of Object.entries(markers)) {
|
||||||
if (!text) break;
|
if (!text) break
|
||||||
if (!text.includes(marker)) continue;
|
if (!text.includes(marker)) continue
|
||||||
let before;
|
let before
|
||||||
[before, text] = text.split(marker);
|
;[before, text] = text.split(marker)
|
||||||
children = [
|
children = [
|
||||||
...children.slice(0, i),
|
...children.slice(0, i),
|
||||||
before,
|
before,
|
||||||
value,
|
value,
|
||||||
text,
|
text,
|
||||||
...children.slice(i + 1)
|
...children.slice(i + 1),
|
||||||
];
|
]
|
||||||
i += 2;
|
i += 2
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let attributes = {}
|
||||||
|
for (const attr of [...elm.attributes]) {
|
||||||
|
let val = attr.nodeValue
|
||||||
|
if (val in markers) val = markers[val]
|
||||||
|
attributes[attr.name] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
return h(nodename, attributes, children)
|
||||||
}
|
}
|
||||||
|
|
||||||
let attributes = {};
|
return wraph(dom.body.children[0])
|
||||||
for (const attr of [...elm.attributes]) {
|
|
||||||
let val = attr.nodeValue;
|
|
||||||
if (val in markers)
|
|
||||||
val = markers[val];
|
|
||||||
attributes[attr.name] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
return h(nodename, attributes, children);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wraph(dom.body.children[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
675
src/js.js
675
src/js.js
|
@ -1,17 +1,17 @@
|
||||||
import { assert } from "./asserts";
|
import { assert } from './asserts'
|
||||||
|
|
||||||
// enables a small terser optimization
|
// enables a small terser optimization
|
||||||
let document = self.document;
|
let document = self.document
|
||||||
|
|
||||||
let Fragment = Symbol();
|
let Fragment = Symbol()
|
||||||
|
|
||||||
// We add some extra properties into various objects throughout, better to use symbols and not interfere. this is just a tiny optimization
|
// We add some extra properties into various objects throughout, better to use symbols and not interfere. this is just a tiny optimization
|
||||||
let [USE_MAPFN, TARGET, PROXY, STEPS, LISTENERS, IF] = [, , , , , ,].fill().map(Symbol);
|
let [USE_MAPFN, TARGET, PROXY, STEPS, LISTENERS, IF] = [, , , , , ,]
|
||||||
|
.fill()
|
||||||
|
.map(Symbol)
|
||||||
|
|
||||||
// whether to return the true value from a stateful object or a "trap" containing the pointer
|
// whether to return the true value from a stateful object or a "trap" containing the pointer
|
||||||
let __use_trap = false;
|
let __use_trap = false
|
||||||
|
|
||||||
|
|
||||||
// Say you have some code like
|
// Say you have some code like
|
||||||
//// let state = stateful({
|
//// let state = stateful({
|
||||||
|
@ -29,399 +29,418 @@ let __use_trap = false;
|
||||||
// - the JSX factory h() is now passed the trap, which essentially contains a set of pointers pointing to the theoretical value of b
|
// - the JSX factory h() is now passed the trap, which essentially contains a set of pointers pointing to the theoretical value of b
|
||||||
// - with the setter on the stateful proxy, we can listen to any change in any of the nested layers and call whatever listeners registered
|
// - with the setter on the stateful proxy, we can listen to any change in any of the nested layers and call whatever listeners registered
|
||||||
// - the result is full intuitive reactivity with minimal overhead
|
// - the result is full intuitive reactivity with minimal overhead
|
||||||
Object.defineProperty(window, "use", {
|
Object.defineProperty(window, 'use', {
|
||||||
get: () => {
|
get: () => {
|
||||||
__use_trap = true;
|
__use_trap = true
|
||||||
return (ptr, mapping, ...rest) => {
|
return (ptr, mapping, ...rest) => {
|
||||||
if (ptr instanceof Array) return usestr(ptr, mapping, ...rest);
|
if (ptr instanceof Array) return usestr(ptr, mapping, ...rest)
|
||||||
assert(isDLPtr(ptr), "a value was passed into use() that was not part of a stateful context");
|
assert(
|
||||||
__use_trap = false;
|
isDLPtr(ptr),
|
||||||
if (mapping) ptr[USE_MAPFN] = mapping;
|
'a value was passed into use() that was not part of a stateful context'
|
||||||
return ptr;
|
)
|
||||||
};
|
__use_trap = false
|
||||||
}
|
if (mapping) ptr[USE_MAPFN] = mapping
|
||||||
});
|
return ptr
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
const usestr = (strings, ...values) => {
|
const usestr = (strings, ...values) => {
|
||||||
__use_trap = false;
|
__use_trap = false
|
||||||
|
|
||||||
let state = stateful({});
|
let state = stateful({})
|
||||||
const flattened_template = [];
|
const flattened_template = []
|
||||||
for (const i in strings) {
|
for (const i in strings) {
|
||||||
flattened_template.push(strings[i]);
|
flattened_template.push(strings[i])
|
||||||
if (values[i]) {
|
if (values[i]) {
|
||||||
const prop = values[i];
|
const prop = values[i]
|
||||||
|
|
||||||
if (isDLPtr(prop)) {
|
if (isDLPtr(prop)) {
|
||||||
const current_i = flattened_template.length;
|
const current_i = flattened_template.length
|
||||||
let oldparsed;
|
let oldparsed
|
||||||
handle(prop, (val) => {
|
handle(prop, (val) => {
|
||||||
flattened_template[current_i] = String(val);
|
flattened_template[current_i] = String(val)
|
||||||
let parsed = flattened_template.join("");
|
let parsed = flattened_template.join('')
|
||||||
if (parsed != oldparsed)
|
if (parsed != oldparsed) state.string = parsed
|
||||||
state.string = parsed
|
oldparsed = parsed
|
||||||
oldparsed = parsed;
|
})
|
||||||
});
|
} else {
|
||||||
} else {
|
flattened_template.push(String(prop))
|
||||||
flattened_template.push(String(prop));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
state.string = flattened_template.join("");
|
state.string = flattened_template.join('')
|
||||||
|
|
||||||
return use(state.string);
|
return use(state.string)
|
||||||
};
|
}
|
||||||
Object.assign(window, { isDLPtr, h, stateful, handle, $if, Fragment });
|
Object.assign(window, { isDLPtr, h, stateful, handle, $if, Fragment })
|
||||||
|
|
||||||
let TRAPS = new Map;
|
let TRAPS = new Map()
|
||||||
// This wraps the target in a proxy, doing 2 things:
|
// This wraps the target in a proxy, doing 2 things:
|
||||||
// - whenever a property is accessed, return a "trap" that catches and records accessors
|
// - whenever a property is accessed, return a "trap" that catches and records accessors
|
||||||
// - whenever a property is set, notify the subscribed listeners
|
// - whenever a property is set, notify the subscribed listeners
|
||||||
// This is what makes our "pass-by-reference" magic work
|
// This is what makes our "pass-by-reference" magic work
|
||||||
function stateful(target, hook) {
|
function stateful(target, hook) {
|
||||||
assert(isobj(target), "stateful() requires an object");
|
assert(isobj(target), 'stateful() requires an object')
|
||||||
target[LISTENERS] = [];
|
target[LISTENERS] = []
|
||||||
target[TARGET] = target;
|
target[TARGET] = target
|
||||||
let TOPRIMITIVE = Symbol.toPrimitive;
|
let TOPRIMITIVE = Symbol.toPrimitive
|
||||||
|
|
||||||
let proxy = new Proxy(target, {
|
let proxy = new Proxy(target, {
|
||||||
get(target, property, proxy) {
|
get(target, property, proxy) {
|
||||||
if (__use_trap) {
|
if (__use_trap) {
|
||||||
let sym = Symbol();
|
let sym = Symbol()
|
||||||
let trap = new Proxy({
|
let trap = new Proxy(
|
||||||
[TARGET]: target,
|
{
|
||||||
[PROXY]: proxy,
|
[TARGET]: target,
|
||||||
[STEPS]: [property],
|
[PROXY]: proxy,
|
||||||
[TOPRIMITIVE]: _ => sym,
|
[STEPS]: [property],
|
||||||
}, {
|
[TOPRIMITIVE]: (_) => sym,
|
||||||
get(target, property) {
|
},
|
||||||
if ([TARGET, PROXY, STEPS, USE_MAPFN, TOPRIMITIVE].includes(property)) return target[property];
|
{
|
||||||
property = TRAPS.get(property) || property;
|
get(target, property) {
|
||||||
target[STEPS].push(property);
|
if (
|
||||||
return trap;
|
[
|
||||||
}
|
TARGET,
|
||||||
});
|
PROXY,
|
||||||
TRAPS.set(sym, trap);
|
STEPS,
|
||||||
|
USE_MAPFN,
|
||||||
|
TOPRIMITIVE,
|
||||||
|
].includes(property)
|
||||||
|
)
|
||||||
|
return target[property]
|
||||||
|
property = TRAPS.get(property) || property
|
||||||
|
target[STEPS].push(property)
|
||||||
|
return trap
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
TRAPS.set(sym, trap)
|
||||||
|
|
||||||
return trap;
|
return trap
|
||||||
}
|
}
|
||||||
return Reflect.get(target, property, proxy);
|
return Reflect.get(target, property, proxy)
|
||||||
},
|
},
|
||||||
set(target, property, val) {
|
set(target, property, val) {
|
||||||
if (hook) hook(target, property, val);
|
if (hook) hook(target, property, val)
|
||||||
let trap = Reflect.set(target, property, val);
|
let trap = Reflect.set(target, property, val)
|
||||||
for (let listener of target[LISTENERS]) {
|
for (let listener of target[LISTENERS]) {
|
||||||
listener(target, property, val);
|
listener(target, property, val)
|
||||||
}
|
}
|
||||||
return trap;
|
return trap
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
return proxy;
|
return proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
let isobj = (o) => o instanceof Object;
|
let isobj = (o) => o instanceof Object
|
||||||
let isfn = (o) => typeof o === "function";
|
let isfn = (o) => typeof o === 'function'
|
||||||
function isDLPtr(arr) {
|
function isDLPtr(arr) {
|
||||||
return isobj(arr) && TARGET in arr
|
return isobj(arr) && TARGET in arr
|
||||||
}
|
}
|
||||||
|
|
||||||
function $if(condition, then, otherwise) {
|
function $if(condition, then, otherwise) {
|
||||||
otherwise ??= document.createTextNode("");
|
otherwise ??= document.createTextNode('')
|
||||||
if (!isDLPtr(condition)) return condition ? then : otherwise;
|
if (!isDLPtr(condition)) return condition ? then : otherwise
|
||||||
|
|
||||||
return { [IF]: condition, then, otherwise };
|
return { [IF]: condition, then, otherwise }
|
||||||
}
|
}
|
||||||
|
|
||||||
// This lets you subscribe to a stateful object
|
// This lets you subscribe to a stateful object
|
||||||
function handle(ptr, callback) {
|
function handle(ptr, callback) {
|
||||||
assert(isDLPtr(ptr), "handle() requires a stateful object");
|
assert(isDLPtr(ptr), 'handle() requires a stateful object')
|
||||||
assert(isfn(callback), "handle() requires a callback function");
|
assert(isfn(callback), 'handle() requires a callback function')
|
||||||
let step, resolvedSteps = [];
|
let step,
|
||||||
|
resolvedSteps = []
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
let val = ptr[TARGET];
|
let val = ptr[TARGET]
|
||||||
for (step of resolvedSteps) {
|
for (step of resolvedSteps) {
|
||||||
val = val[step];
|
val = val[step]
|
||||||
if (!isobj(val)) break;
|
if (!isobj(val)) break
|
||||||
}
|
|
||||||
|
|
||||||
let mapfn = ptr[USE_MAPFN];
|
|
||||||
if (mapfn) val = mapfn(val);
|
|
||||||
callback(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// inject ourselves into nested objects
|
|
||||||
let curry = (target, i) => function subscription(tgt, prop, val) {
|
|
||||||
if (prop === resolvedSteps[i] && target === tgt) {
|
|
||||||
update();
|
|
||||||
|
|
||||||
if (isobj(val)) {
|
|
||||||
let v = val[LISTENERS];
|
|
||||||
if (v && !v.includes(subscription)) {
|
|
||||||
v.push(curry(val[TARGET], i + 1));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
let mapfn = ptr[USE_MAPFN]
|
||||||
|
if (mapfn) val = mapfn(val)
|
||||||
|
callback(val)
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
// inject ourselves into nested objects
|
||||||
|
let curry = (target, i) =>
|
||||||
|
function subscription(tgt, prop, val) {
|
||||||
|
if (prop === resolvedSteps[i] && target === tgt) {
|
||||||
|
update()
|
||||||
|
|
||||||
// imagine we have a `use(state.a[state.b])`
|
if (isobj(val)) {
|
||||||
// simply recursively resolve any of the intermediate steps until we get to the final value
|
let v = val[LISTENERS]
|
||||||
// this will "misfire" occassionaly with a scenario like state.a[state.b][state.c] and call the listener more than needed
|
if (v && !v.includes(subscription)) {
|
||||||
// it is up to the caller to not implode
|
v.push(curry(val[TARGET], i + 1))
|
||||||
for (let i in ptr[STEPS]) {
|
}
|
||||||
let step = ptr[STEPS][i];
|
}
|
||||||
if (isobj(step) && step[TARGET]) {
|
}
|
||||||
handle(step, val => {
|
}
|
||||||
resolvedSteps[i] = val;
|
|
||||||
update();
|
// imagine we have a `use(state.a[state.b])`
|
||||||
});
|
// simply recursively resolve any of the intermediate steps until we get to the final value
|
||||||
continue;
|
// this will "misfire" occassionaly with a scenario like state.a[state.b][state.c] and call the listener more than needed
|
||||||
|
// it is up to the caller to not implode
|
||||||
|
for (let i in ptr[STEPS]) {
|
||||||
|
let step = ptr[STEPS][i]
|
||||||
|
if (isobj(step) && step[TARGET]) {
|
||||||
|
handle(step, (val) => {
|
||||||
|
resolvedSteps[i] = val
|
||||||
|
update()
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resolvedSteps[i] = step
|
||||||
}
|
}
|
||||||
resolvedSteps[i] = step;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sub = curry(ptr[TARGET], 0);
|
let sub = curry(ptr[TARGET], 0)
|
||||||
ptr[TARGET][LISTENERS].push(sub);
|
ptr[TARGET][LISTENERS].push(sub)
|
||||||
|
|
||||||
sub(ptr[TARGET], resolvedSteps[0], ptr[TARGET][resolvedSteps[0]]);
|
sub(ptr[TARGET], resolvedSteps[0], ptr[TARGET][resolvedSteps[0]])
|
||||||
}
|
}
|
||||||
|
|
||||||
function JSXAddFixedWrapper(ptr, cb, $if) {
|
function JSXAddFixedWrapper(ptr, cb, $if) {
|
||||||
let before, appended, first, flag;
|
let before, appended, first, flag
|
||||||
handle(ptr, val => {
|
handle(ptr, (val) => {
|
||||||
first = appended?.[0];
|
first = appended?.[0]
|
||||||
if (first)
|
if (first) before = first.previousSibling || (flag = first.parentNode)
|
||||||
before = first.previousSibling || (flag = first.parentNode);
|
if (appended) appended.forEach((a) => a.remove())
|
||||||
if (appended)
|
|
||||||
appended.forEach(a => a.remove());
|
|
||||||
|
|
||||||
appended = JSXAddChild($if ? (val ? $if.then : $if.otherwise) : val, el => {
|
appended = JSXAddChild(
|
||||||
if (before) {
|
$if ? (val ? $if.then : $if.otherwise) : val,
|
||||||
if (flag) {
|
(el) => {
|
||||||
before.prepend(el)
|
if (before) {
|
||||||
flag = null;
|
if (flag) {
|
||||||
}
|
before.prepend(el)
|
||||||
else before.after(el);
|
flag = null
|
||||||
before = el;
|
} else before.after(el)
|
||||||
}
|
before = el
|
||||||
else cb(el)
|
} else cb(el)
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a function that sets a reference
|
// returns a function that sets a reference
|
||||||
// the currying is a small optimization
|
// the currying is a small optimization
|
||||||
let curryset = ptr => val => {
|
let curryset = (ptr) => (val) => {
|
||||||
let next = ptr[PROXY];
|
let next = ptr[PROXY]
|
||||||
let steps = ptr[STEPS];
|
let steps = ptr[STEPS]
|
||||||
let i = 0;
|
let i = 0
|
||||||
for (; i < steps.length - 1; i++) {
|
for (; i < steps.length - 1; i++) {
|
||||||
next = next[steps[i]];
|
next = next[steps[i]]
|
||||||
if (!isobj(next)) return;
|
if (!isobj(next)) return
|
||||||
}
|
}
|
||||||
next[steps[i]] = val;
|
next[steps[i]] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual JSX factory. Responsible for creating the HTML elements and all of the *reactive* syntactic sugar
|
// Actual JSX factory. Responsible for creating the HTML elements and all of the *reactive* syntactic sugar
|
||||||
function h(type, props, ...children) {
|
function h(type, props, ...children) {
|
||||||
if (type == Fragment) return children;
|
if (type == Fragment) return children
|
||||||
if (typeof type == "function") {
|
if (typeof type == 'function') {
|
||||||
// functional components. create the stateful object
|
// functional components. create the stateful object
|
||||||
let newthis = stateful(Object.create(type.prototype));
|
let newthis = stateful(Object.create(type.prototype))
|
||||||
|
|
||||||
|
for (let name in props) {
|
||||||
|
let ptr = props[name]
|
||||||
|
if (name.startsWith('bind:')) {
|
||||||
|
assert(
|
||||||
|
isDLPtr(ptr),
|
||||||
|
'bind: requires a reference pointer from use'
|
||||||
|
)
|
||||||
|
|
||||||
|
let set = curryset(ptr)
|
||||||
|
let propname = name.substring(5)
|
||||||
|
if (propname == 'this') {
|
||||||
|
set(newthis)
|
||||||
|
} else {
|
||||||
|
// component two way data binding!! (exact same behavior as svelte:bind)
|
||||||
|
let isRecursive = false
|
||||||
|
|
||||||
|
handle(ptr, (value) => {
|
||||||
|
if (isRecursive) {
|
||||||
|
isRecursive = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isRecursive = true
|
||||||
|
newthis[propname] = value
|
||||||
|
})
|
||||||
|
handle(use(newthis[propname]), (value) => {
|
||||||
|
if (isRecursive) {
|
||||||
|
isRecursive = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isRecursive = true
|
||||||
|
set(value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
delete props[name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.assign(newthis, props)
|
||||||
|
|
||||||
|
newthis.children = []
|
||||||
|
for (let child of children) {
|
||||||
|
JSXAddChild(child, newthis.children.push.bind(newthis.children))
|
||||||
|
}
|
||||||
|
|
||||||
|
let elm = type.apply(newthis)
|
||||||
|
elm.$ = newthis
|
||||||
|
newthis.root = elm
|
||||||
|
if (newthis.css) {
|
||||||
|
let cl = elm.classList
|
||||||
|
cl.add(newthis.css)
|
||||||
|
cl.add('self')
|
||||||
|
}
|
||||||
|
elm.setAttribute('data-component', type.name)
|
||||||
|
if (typeof newthis.mount === 'function') newthis.mount()
|
||||||
|
return elm
|
||||||
|
}
|
||||||
|
|
||||||
|
let xmlns = props?.xmlns
|
||||||
|
let elm = xmlns
|
||||||
|
? document.createElementNS(xmlns, type)
|
||||||
|
: document.createElement(type)
|
||||||
|
|
||||||
|
for (let child of children) {
|
||||||
|
let cond = child && !isDLPtr(child) && child[IF]
|
||||||
|
let bappend = elm.append.bind(elm)
|
||||||
|
if (cond) {
|
||||||
|
JSXAddFixedWrapper(cond, bappend, child)
|
||||||
|
} else JSXAddChild(child, bappend)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props) return elm
|
||||||
|
|
||||||
|
let useProp = (name, callback) => {
|
||||||
|
if (!(name in props)) return
|
||||||
|
let prop = props[name]
|
||||||
|
callback(prop)
|
||||||
|
delete props[name]
|
||||||
|
}
|
||||||
|
|
||||||
for (let name in props) {
|
for (let name in props) {
|
||||||
let ptr = props[name];
|
let ptr = props[name]
|
||||||
if (name.startsWith("bind:")) {
|
if (name.startsWith('bind:')) {
|
||||||
assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
|
assert(isDLPtr(ptr), 'bind: requires a reference pointer from use')
|
||||||
|
let propname = name.substring(5)
|
||||||
|
|
||||||
let set = curryset(ptr);
|
// create the function to set the value of the pointer
|
||||||
let propname = name.substring(5);
|
let set = curryset(ptr)
|
||||||
if (propname == "this") {
|
if (propname == 'this') {
|
||||||
set(newthis);
|
set(elm)
|
||||||
} else {
|
} else if (propname == 'value') {
|
||||||
// component two way data binding!! (exact same behavior as svelte:bind)
|
handle(ptr, (value) => (elm.value = value))
|
||||||
let isRecursive = false;
|
elm.addEventListener('change', () => set(elm.value))
|
||||||
|
} else if (propname == 'checked') {
|
||||||
handle(ptr, value => {
|
handle(ptr, (value) => (elm.checked = value))
|
||||||
if (isRecursive) {
|
elm.addEventListener('click', () => set(elm.checked))
|
||||||
isRecursive = false;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
isRecursive = true;
|
delete props[name]
|
||||||
newthis[propname] = value
|
}
|
||||||
});
|
if (name == 'style' && isobj(ptr)) {
|
||||||
handle(use(newthis[propname]), value => {
|
for (let key in ptr) {
|
||||||
if (isRecursive) {
|
let prop = ptr[key]
|
||||||
isRecursive = false;
|
if (isDLPtr(prop)) {
|
||||||
return;
|
handle(prop, (value) => (elm.style[key] = value))
|
||||||
}
|
} else {
|
||||||
isRecursive = true;
|
elm.style[key] = prop
|
||||||
set(value);
|
}
|
||||||
});
|
}
|
||||||
|
delete props[name]
|
||||||
}
|
}
|
||||||
delete props[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Object.assign(newthis, props);
|
|
||||||
|
|
||||||
newthis.children = [];
|
|
||||||
for (let child of children) {
|
|
||||||
JSXAddChild(child, newthis.children.push.bind(newthis.children));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let elm = type.apply(newthis);
|
useProp('class', (classlist) => {
|
||||||
elm.$ = newthis;
|
assert(
|
||||||
newthis.root = elm;
|
typeof classlist === 'string' || classlist instanceof Array,
|
||||||
if (newthis.css) {
|
'class must be a string or array'
|
||||||
let cl = elm.classList
|
)
|
||||||
cl.add(newthis.css);
|
if (typeof classlist === 'string') {
|
||||||
cl.add("self");
|
elm.setAttribute('class', classlist)
|
||||||
}
|
return
|
||||||
elm.setAttribute("data-component", type.name);
|
}
|
||||||
if (typeof newthis.mount === "function")
|
|
||||||
newthis.mount();
|
|
||||||
return elm;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (isDLPtr(classlist)) {
|
||||||
|
handle(classlist, (classname) =>
|
||||||
|
elm.setAttribute('class', classname)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let xmlns = props?.xmlns;
|
for (let name of classlist) {
|
||||||
let elm = xmlns ? document.createElementNS(xmlns, type) : document.createElement(type);
|
if (isDLPtr(name)) {
|
||||||
|
let oldvalue = null
|
||||||
|
handle(name, (value) => {
|
||||||
|
if (typeof oldvalue === 'string') {
|
||||||
|
elm.classList.remove(oldvalue)
|
||||||
|
}
|
||||||
|
elm.classList.add(value)
|
||||||
|
oldvalue = value
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
elm.classList.add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// apply the non-reactive properties
|
||||||
for (let child of children) {
|
for (let name in props) {
|
||||||
let cond = child && !isDLPtr(child) && child[IF];
|
let prop = props[name]
|
||||||
let bappend = elm.append.bind(elm);
|
|
||||||
if (cond) {
|
|
||||||
JSXAddFixedWrapper(cond, bappend, child);
|
|
||||||
} else
|
|
||||||
JSXAddChild(child, bappend);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props) return elm;
|
|
||||||
|
|
||||||
let useProp = (name, callback) => {
|
|
||||||
if (!(name in props)) return;
|
|
||||||
let prop = props[name];
|
|
||||||
callback(prop);
|
|
||||||
delete props[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let name in props) {
|
|
||||||
let ptr = props[name];
|
|
||||||
if (name.startsWith("bind:")) {
|
|
||||||
assert(isDLPtr(ptr), "bind: requires a reference pointer from use");
|
|
||||||
let propname = name.substring(5);
|
|
||||||
|
|
||||||
// create the function to set the value of the pointer
|
|
||||||
let set = curryset(ptr);
|
|
||||||
if (propname == "this") {
|
|
||||||
set(elm);
|
|
||||||
} else if (propname == "value") {
|
|
||||||
handle(ptr, value => elm.value = value);
|
|
||||||
elm.addEventListener("change", () => set(elm.value))
|
|
||||||
} else if (propname == "checked") {
|
|
||||||
handle(ptr, value => elm.checked = value);
|
|
||||||
elm.addEventListener("click", () => set(elm.checked))
|
|
||||||
}
|
|
||||||
delete props[name];
|
|
||||||
}
|
|
||||||
if (name == "style" && isobj(ptr)) {
|
|
||||||
for (let key in ptr) {
|
|
||||||
let prop = ptr[key];
|
|
||||||
if (isDLPtr(prop)) {
|
if (isDLPtr(prop)) {
|
||||||
handle(prop, value => elm.style[key] = value);
|
handle(prop, (val) => {
|
||||||
|
JSXAddAttributes(elm, name, val)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
elm.style[key] = prop;
|
JSXAddAttributes(elm, name, prop)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
delete props[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useProp("class", classlist => {
|
|
||||||
assert(typeof classlist === "string" || classlist instanceof Array, "class must be a string or array");
|
|
||||||
if (typeof classlist === "string") {
|
|
||||||
elm.setAttribute("class", classlist);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDLPtr(classlist)) {
|
// hack to fix svgs
|
||||||
handle(classlist, classname => elm.setAttribute("class", classname));
|
if (xmlns) elm.innerHTML = elm.innerHTML
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let name of classlist) {
|
return elm
|
||||||
if (isDLPtr(name)) {
|
|
||||||
let oldvalue = null;
|
|
||||||
handle(name, value => {
|
|
||||||
if (typeof oldvalue === "string") {
|
|
||||||
elm.classList.remove(oldvalue);
|
|
||||||
}
|
|
||||||
elm.classList.add(value);
|
|
||||||
oldvalue = value;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
elm.classList.add(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// apply the non-reactive properties
|
|
||||||
for (let name in props) {
|
|
||||||
let prop = props[name];
|
|
||||||
if (isDLPtr(prop)) {
|
|
||||||
handle(prop, (val) => {
|
|
||||||
JSXAddAttributes(elm, name, val);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
JSXAddAttributes(elm, name, prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hack to fix svgs
|
|
||||||
if (xmlns)
|
|
||||||
elm.innerHTML = elm.innerHTML
|
|
||||||
|
|
||||||
return elm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// glue for nested children
|
// glue for nested children
|
||||||
function JSXAddChild(child, cb) {
|
function JSXAddChild(child, cb) {
|
||||||
let childchild, elms, node;
|
let childchild, elms, node
|
||||||
if (isDLPtr(child)) {
|
if (isDLPtr(child)) {
|
||||||
JSXAddFixedWrapper(child, cb);
|
JSXAddFixedWrapper(child, cb)
|
||||||
} else if (child instanceof Node) {
|
} else if (child instanceof Node) {
|
||||||
cb(child);
|
cb(child)
|
||||||
return [child];
|
return [child]
|
||||||
} else if (child instanceof Array) {
|
} else if (child instanceof Array) {
|
||||||
elms = [];
|
elms = []
|
||||||
for (childchild of child) {
|
for (childchild of child) {
|
||||||
elms = elms.concat(JSXAddChild(childchild, cb));
|
elms = elms.concat(JSXAddChild(childchild, cb))
|
||||||
|
}
|
||||||
|
if (!elms[0]) elms = JSXAddChild('', cb)
|
||||||
|
return elms
|
||||||
|
} else {
|
||||||
|
node = document.createTextNode(child)
|
||||||
|
cb(node)
|
||||||
|
return [node]
|
||||||
}
|
}
|
||||||
if (!elms[0]) elms = JSXAddChild("", cb);
|
|
||||||
return elms;
|
|
||||||
} else {
|
|
||||||
node = document.createTextNode(child);
|
|
||||||
cb(node);
|
|
||||||
return [node];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Where properties are assigned to elements, and where the *non-reactive* syntax sugar goes
|
// Where properties are assigned to elements, and where the *non-reactive* syntax sugar goes
|
||||||
function JSXAddAttributes(elm, name, prop) {
|
function JSXAddAttributes(elm, name, prop) {
|
||||||
if (name.startsWith("on:")) {
|
if (name.startsWith('on:')) {
|
||||||
assert(typeof prop === "function", "on: requires a function");
|
assert(typeof prop === 'function', 'on: requires a function')
|
||||||
let names = name.substring(3);
|
let names = name.substring(3)
|
||||||
for (let name of names.split("$")) {
|
for (let name of names.split('$')) {
|
||||||
elm.addEventListener(name, (...args) => {
|
elm.addEventListener(name, (...args) => {
|
||||||
self.$el = elm;
|
self.$el = elm
|
||||||
prop(...args);
|
prop(...args)
|
||||||
});
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
elm.setAttribute(name, prop);
|
elm.setAttribute(name, prop)
|
||||||
}
|
}
|
||||||
|
|
16
src/store.js
16
src/store.js
|
@ -1,12 +1,12 @@
|
||||||
Object.assign(window, { $store });
|
Object.assign(window, { $store })
|
||||||
function $store(target, ident, type) {
|
function $store(target, ident, type) {
|
||||||
let stored = localStorage.getItem(ident);
|
let stored = localStorage.getItem(ident)
|
||||||
target = JSON.parse(stored) ?? target;
|
target = JSON.parse(stored) ?? target
|
||||||
|
|
||||||
addEventListener("beforeunload", () => {
|
addEventListener('beforeunload', () => {
|
||||||
console.info("[dreamland.js]: saving " + ident);
|
console.info('[dreamland.js]: saving ' + ident)
|
||||||
localStorage.setItem(ident, JSON.stringify(target));
|
localStorage.setItem(ident, JSON.stringify(target))
|
||||||
});
|
})
|
||||||
|
|
||||||
return stateful(target);
|
return stateful(target)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,23 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"lib": ["ESNext", "dom"],
|
||||||
|
"outDir": "./examples/lib",
|
||||||
|
"removeComments": true,
|
||||||
|
"target": "ES6",
|
||||||
|
|
||||||
"lib": [
|
"jsx": "react",
|
||||||
"ESNext",
|
"jsxFactory": "h",
|
||||||
"dom"
|
"jsxFragmentFactory": "Fragment",
|
||||||
],
|
|
||||||
"outDir": "./examples/lib",
|
|
||||||
"removeComments": true,
|
|
||||||
"target": "ES6",
|
|
||||||
|
|
||||||
"jsx":"react",
|
"sourceMap": true,
|
||||||
"jsxFactory":"h",
|
"sourceRoot": "",
|
||||||
"jsxFragmentFactory":"Fragment",
|
|
||||||
|
|
||||||
"sourceMap": true,
|
"alwaysStrict": false,
|
||||||
"sourceRoot": "",
|
"noImplicitAny": false,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
|
||||||
"alwaysStrict": false,
|
"noUncheckedIndexedAccess": true
|
||||||
"noImplicitAny": false,
|
},
|
||||||
"strictNullChecks":false,
|
"include": ["./examples/*.ts", "./examples/*.tsx", "./DreamlandJS.d.ts"],
|
||||||
|
"exclude": ["node_modules/**/*"]
|
||||||
"noUncheckedIndexedAccess": true
|
|
||||||
},
|
|
||||||
"include": ["./examples/*.ts","./examples/*.tsx", "./DreamlandJS.d.ts"],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules/**/*"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue