Comments (13)
Recap.
$(el, el => {fx()})
acts very similar towith($(el)) { fx() }
, meaning that the effect is a feature of wrapped observer.$(el).init(() => () => {})
is the same as$(el, el => {})
, mb we could even shrink that syntax to just$(target)[effect]()
, and then thelet aspect = $(el)
would indeed return some live-wrapper of all DOM instances ever going to appear, a piece of behavior (unlike jquery one-timer).<${target}></>
are possibly prohibited inhtml
, at least not for portal uses, but for indicating order of insertion.
from spect.
De-resolution.
HTM has prohibited <...>
tags, besides that adds a dimension of complexity required to learn.
So we're with <div>${el.childNodes}</div>
for html reducers. Which makes sense extending to <${el}></>
, enabling portals as <${target}></>
.
Prohibited reducers remove the drawbacks of unclear reducer context, making html
a single entry.
// creates raw html
let els = html`<div></div>`
// mounts html on target
html`<${el}><div></div></>`
// mounts target to html, el can be a fragment/array/fn etc
html`<div>${el}</div>`
// mounts target to html, applies props/children to target
// watch out: `el` is going to have empty children
html`<div><${el}/></div>`
- not as elegant default effect html
<${el}>content</>
. (although apparent).
- useful as independent tool indeed.
Since <${el}/>
isn't as elegant and also adds to confusion of wrapping, we can reuse known atomico pattern <host></host>
. That allows graceful syntax, scoping inner styles via :host
and explicitly separates current context element. Although, that renders plain multiple elements useless:
html`<host><a></a></host><b></b>`
// is effectively meaningless. Does it return just a list of elements?
So again, what's bad about $(el, el => <div></div>)
?
- unobvious where the dom is attached, even confusing
- complex syntax.
Actually, look again. ⭐
let [hostEl, portalEl, fooEl: { children: [bar]}] = html`
<host>...</>
<${modal.container}></><foo>
<bar/></foo>`
// isn't that super-cool way to get refs?
// we can possibly introduce selectors via proxy:
let { '#x': xEl, '.s': sClassEls } = html`...`
// mb even modifying via proxy as
let [x, y, [z]] = html`<x/><y/><><z/></>` // z is first child
Looks brilliant (if that's not an illusion).
from spect.
host
is from web-components terminology, no need to engage it here.
For most of nested components returning html
works as unwrapped mount:
const Log = (data) => html`<p>${data}</p>`
from spect.
Another turn.
html`<${mount}>content</>`
creates contradictory situation where html
is no more an effect: $(target).html`now requires wrapping here`
- not elegant.
Also, making it anonymous effect creating detached content creates bottleneck on rendering functions:
function A () {
html`<${B}></>`
}
function B () {
// here B can't do DOM diff, it is required to build fully detached raw HTML from scratch.
return html`content`
}
Instead, having pure HTML effect is not contradictory to passing selectors internally.
function A () {
html`<${B}></>`
}
function B () {
// no contradiction, effect is applied to B [custom] element
html`content`
}
So let's opt for html as effect, but returning created content for obtaining refs and portal-mount.
el => {
let [a, b, c] = html`
<a/>
<${B} localTarget/>
<${externalTarget} ...attrs/>
`
}
from spect.
Should multiple html
instances rewrite each other, or augment?
el => {
html`Current content`
...
html`<${portal}>Content</>`
}
How to provide combined html
, and is that required?
? What if we engage yield mechanism for augmented html?
// multiple renders at the time of yield
function* (el) {
yield html`Content`
yield html`<${portal}>Content</>`
}
// single planned render, html replace planned rendering
(el) => {
html`Content`
html`<${portal}>Content</>`
}
Should h
render itself instantly on the time of call, or plan total VDOM update by the end of render?
- instant rendering allows to read reference straight ahead
? no clear way to mount on call
- delayed rendering allows batching and is easier to implement effect
- delayed rendering corresponds to react practice
- delayed rendering is confusing if
html
effect takes place before other effect.
. for throttle and raf there should be separate effects. That should be as easy as raf(fn)
- to plan a set of effects for the next frame.
? could we possibly provide update
effect, flushing every planned action? Would that be nice?
el => {
<div id='x'>content</div>
render()
let a = el.querySelector('#x')
}
- no, that doesn't look nice. Better have some h-related renderer:
el => {
html(<div id='x'>content</div>)
let a = el.querySelector('#x')
}
✔ yes, that's right. html
should be able to take VDOM as an argument, and h
is just a JSX-constructor.
import $, { html, h } from 'spect'
$(app, el => {
html(<div id='123'/>)
})
from spect.
Ok, html is now direct effect again #107.
Unlike other effects, it could be
html`<${el}>...content</>`
So, just global html rerenderer.
Some questions:
- What should it return?
- How should that render vdom?
- How to create new real content?
Some considerations:
html
is justhtm
builderhtml`<${el}></>`
returns top element or list of elements
We can do everything via domdiff and fully avoid vdom. What's the price of that?
from spect.
Done as direct morphdom implementation.
from spect.
Deresolution.
html is [anyways] can be an effect with observables, so libs like lit-html aren't directly useful here.
html`<time ${{date}}>${ date().toLocaleTimeString() }</time>`
And it automatically binds to observables.
The consideration is - organization of morphing. If <${el}></>
, then morphing classes can be
html`<${el} class=${ clx => [...clx, 'additional-class'] } ${attrs => {...attrs, ...additionalAttrs }}>
${ content => [...content, additionalPiece]}
</>`
from spect.
Another feature of html
effect: it creates an observable from all the internal nodes. That allows to create composable streamable structures.
const time = html`<time date=${ date.toISOString() }>${ date.toLocaleString() }</time>`
const post = article => html`<article>${ article.author }${ article.content }${ article.time }</article>`
const articles = map(data, post) // calc((...items) => items.map(item => post(item)), arr(items))
const list = html`<div class="articles">${ articles }</div>`
From here, for list, we need a special structure, returning observable with mapped items.
But in general, instead of dom diffing (mb internally), the effect subscribes to all input observables.
from spect.
Сreates an observable node, that morphs whenever content changes
let div = html`<div>${ content }</div>`
Morphs existing node whenever props or content changes
Passing el === null removes node
let el = html`<${el} ...${props}>${ content }</>`
Can also be useful to map existing props/content?
Or that contradicts to observables?
html`<${el} ...${props => {...props}}>${c => c}</>`
Or should we just guess by default extending existing element's props, instead of forcing template?
html`<${el} additional='a' class='${ clsx => clsx }'></>`
Is there contradiction at all between observable and reducer?
// state
const clsx = state('a b c')
html`<div class=${clsx} />`
// rendering html sets clsx state, so `clsx` is controlled by html
// if we detect that clsx is async iterable, it's possible to stream clsx to html instead
// observable
const obs = o.value('a b c')
html`<div class=${obs}/>`
// here, html should subscribe to obs updates by passing a function into `obs`
// passing value as in reducer scenario would just update obs.
// direct fn
html`<div class=${clsx => clsx}/>`
// similar to ref, but can be useless, because it's not reactive
// that makes the whole html just a 1-time 'setter' effect, which isn't 100% correct
! This brings to an idea - connecting observables by passing observables into observables:
let count = state(0)
let count2 = state(count) // just a proxy observable
let prop2 = state(prop1) // proxy for prop1
let el = html`<div>${ count1 }</div>` // live element
from spect.
Ok, that takes long.
Essential cases of html:
1. define live template
let article = html`<article class=${clsx} ${prop} ...${props}>${ content }</article>`
This case is clear.
✔
2. hydrate existing html
html`<${target} class=${clsx} ${prop} ...${props}>${ content }</>`
Here it's not clear - the target is extended or forced to this form.
Since redefining all props is impossible, rewriting only passed ones makes more sense.
But then single props ares till overridden. How to extend them too?
// v1. create classes observable
const cl = clsx(target)
cl(c => c + ' additional')
html`<${target} class=${clx}></>`
// v2. just read target classes - target is known anyways
html`<${target} class="${target.className} additional"/>`
// ⚠️ dangerous
html`<${target} ...${target}/>`
So we don't need prop reducers, since target is available.
✔
from spect.
Due to single-call html nature, we don't even need caching statics, that is perfect match for xhtm.
from spect.
Done in 15.0.0
from spect.
Related Issues (20)
- h: cache element by stack instead of creating anew HOT 1
- h: drop parser, use htm HOT 2
- v → f, v. Shaving off API, v as proxy HOT 6
- operators HOT 1
- 0b HOT 1
- v ~~as proxy~~ as observable HOT 1
- v: access multiarg values as [0], [1] etc. HOT 1
- $ → selector-collection HOT 2
- h: keyed. Why not morphing? HOT 3
- v(v0, v1, v2) - observables as args HOT 1
- $: pseudos? HOT 1
- h/$ - a nice way to convert data to template? HOT 2
- add Github Corner to spect.ly and fix typo HOT 2
- v23: animevents-only $ HOT 1
- 23.0: API reconsiderations, half-a-year later
- $: stop infinite recursion HOT 1
- $: first run flickers
- $(parent, ‘> child’)
- h: lists; diff algo is underused. Possible syntax HOT 1
- spect@24 breaks integration tests with subscribable-things HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from spect.