DrSensor / DrSensor/nusa

Procedural UI (Island/Widget/SPA)

Open
#32 0 comments 0 reactions 0 assignees View on GitHub
enhancement priority: low
Dominant language
JavaScript
Stars
4
Forks
0
PR merge metrics
No merged PRs in 30d

Description

# Ergonomic Island
- [ ] `accessor(value)` factory
- [ ] `iterate(local_accessor, values => {})`
- [ ] `custom = accessor.of(_: Array | TypedArray | { set(?), get() })`
- [ ] standard JSX factory ++
- [ ] async function as deferred Element
- [ ] async generator as Suspense replacement
- [ ] generator function as either [dynamic](https://www.solidjs.com/tutorial/flow_dynamic?solved) [\](https://crank.js.org/guides/components) or `List()` component
- [ ] `this instanceof Class` where instance is from `@build(Component)` reside
- [ ] ``` bind`accessor` ``` (see SSR/SSG issue)

## single `accessor(value)`
Behind the scene, `accessor(value)` is just a syntactic sugar for `new Accessor(value)` where
```js
const defineAccessor = () => @allocate(Infinity) class {
accessor value
constructor(value) { this.value = value }
valueOf() { return this.value }
}
```
which the `Accessor` class is unique per scopes of Component
```js
let prevC1_a, currentC2_a
function C1() {
const a = accessor()
const b = accessor()

// each variables are differ from others
assert(a.prototype !== b.prototype)

// but each same variables is constructed by the same class
if (prevC1_a) assert(
prevC1_a.prototype === a.prototype
)
prevC1_a = a

// however, it will be different if it came from different scopes although it's in the same order
if (currentC2_a) assert(
currentC2_a.prototype !== a.prototype
)
}

function C2() {
const a = accessor()
const b = accessor()
currentC2_a = a
}
```
> **Warning**: so yeah. The accessor must always be on top and in the same order. Not dynamically created in `if`, in `for`, in ternary operator, created after `await`, created inside callback, etc. Think of it like `super()` constructor.

example
```jsx
const total = accessor(0) // non-iterate()able, behave like signal()

function Button({ value } = {}) {
const count = accessor(value ?? 0) // iterate()able

// increment all every seconds
runOnce(() => setInterval(() =>
iterate(count, (at, i, len) => {
if (+total < 200) {
at[i]++
total++
}
})
), 1e3))

total.value += count
const increment = () =>
total.value += count.value += 1

return
count: {count}

}

function Island() {
return <>
total: {total}
{Array.from(
{ length: 100 },
(i) => ,
)}

}

export default class {
@build(Island)
host //
}
```

## async function and generator
> Inspired from [Crank](https://crank.js.org/guides/async-components) and [Tonic](https://tonicframework.dev)

In async generator, each `yield` and `return` will replace previous `yield`. However, normal generator function will behave the same way as Array of \.

```js
async function IPAddress() {
const res = await fetch("https://api.ipify.org")
const address = await res.text()
return {address}
}

async function *Island() {
yield Loading...

const addr = await fetch("https://api.ipify.org").then(as => as.text())
yield Your IP {addr}

yield
{await fetch("https://api.ipify.org")
.then(as => as.text())} is

// render/print same 100 IP address 😂
return await Promise.all(Array.from(
{ length: 100 },
() => ,
))
}

export default class {
@build(IPAdress)
ip // Loading...

@build(Island)
stressIP //


}
```
Behind the scene, async function is same as async generator where it `yield new Comment(function.name)` before the function is `await`ing.

Basically, that \ is same as:
```js
async function *IPAddress() {
yield new Comment("IPAddress")
const res = await fetch("https://api.ipify.org")
const address = await res.text()
return {address}
}
```

## exposing local data when creating children
Expose local variable for some operation similar on how Svelte directive `let:variable` being used.

```jsx
function ColorManager({ children }) {
const colors = ['red', 'green', 'blue']
if (!("colors" in this)) this.colors = colors
return <>
...
children

}

{({ colors }) =>

    {
    colors.map(color =>
  • {color}

  • )
    }

};
```

## non-JSX DOM builder/factory
Although this feature can be replaced by integrating with other SPA framework, most of them rely on compiler/transformer so there is no harm to experiment new approach.
> **Hint**: check my previous prototype in different repo
>
> Also, always think if certain approach is performance. Think about if JS engine:
> * Cause deopt like switching from IC to MapRecord
> * Can opt like auto inline and dce

REJECTED

```ts
type Children = Array<
| Element
| Primitive
| Text
>

interface HTML {
[key: string]: (
$1?:
| Record>
& { $children:
| Children
| Primitive
| Text }
| Children
| Primitive | Text
| ($: SelfElement) => void,
$2?:
| Children // if $1 not Array
| ($: SelfElement) => void // if $1 not function
) => HTMLElement
}

export const
html = new Proxy,
svg = new Proxy

function text(
value: Primitive | Signal
): Text
function text(
str: string[],
val: Primitive | Signal,
): Text
export function text(
$1: (Primitive | Signal) | string[],
$2?: Primitive | Signal,
): Text { }

export let on = {}

export function attrs(
$: Record>
) { }
export function props(
$: Record
) { }
```
```js
import { defer, use } from "wiles/std"
import { html, svg, text, on } from "wiles/dom/fun-runtime"

const { div, button, h3 } = html

function Component() {
const [c, c_] = use(Counter)

function stopAt10 (e) {
if (c.count++ === 10)
msg([h3(10)]) // auto unbind c.count
btn(e.target.type = "submit")

//on.click = null // specific abort listener of e.target
this.abort$() // self abort stopAt10
}
}

let msg

defer(() => assert(msg() instanceof HTMLDivElement))

return div([//


"click to count",
btn = button({ value: c_.count }, ($) => {//

$.accessKey = "+"
text`${c_.count}++`

$.style = getComputedStyle(msg())
$.style.border = "5px"

on.click = stopAt10
}),//
msg = div(text`count ${c_.count}`),
])//
}
```
> **Note**: something not allowed in micro/macro-task inside `el(?, $ => {/*here/*})`
> * add event listener via `on.event = () => {}`
> * append children via `html.el()` or `text()`
>
> but it still ok if doing thats inside `on.event` and `el(?, $ => {/*here/*})`

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.