Releases: argyleink/prop-for-that
Releases · argyleink/prop-for-that
v0.7.4
Added
keyboardplugin (global) — exposes the soft (on-screen) keyboard's
geometry on:root:--live-keyboard-open(1/0),--live-keyboard-height,
--live-keyboard-width, and--live-keyboard-x/-y/-right/-bottom
(px, client coordinates), so layout can reflow around the keyboard with pure
CSS —padding-block-end: calc(var(--live-keyboard-height) * 1px). Reads exact
geometry from the VirtualKeyboard API on Chromium/Android (it sets
navigator.virtualKeyboard.overlaysContent = truewhile bound — so the
keyboard overlays content instead of resizing the viewport — and restores it on
dispose), and falls back to inferring the height fromvisualViewportshrink on
iOS/Safari (approximate: a pinch-zoom can read as a keyboard). Seeds zeros where
neither API is available. (#6)metaplugin (global) — writes every<meta name|property + content>on
the page as a write-once--const-meta-<slug>(the slug lowercases the name and
collapses runs of non-alphanumerics to a single dash):--const-meta-theme-color,
--const-meta-og-image,--const-meta-color-scheme, etc. — so CSS can read page
metadata directly (background: var(--const-meta-theme-color),
background-image: url(var(--const-meta-og-image))). First match wins per name,
and amedia-conditional meta is skipped when its query doesn't currently match,
so the activetheme-colorvariant is the one written. Read once at bind on the
constcadence, so meta tags swapped in by a framework after bind aren't
tracked. (#7)
Both are tree-shakeable opt-in plugins — import from prop-for-that/plugins, or
let auto load them on demand via data-props-for.
v0.7.3
Changed
- Event-driven element sources no longer use the off-screen viewport gate.
range,field,field-state,form-state,select,color-input, and
imgnow run ungated (gate: false). They only write in response to user
interaction or an imageload— neither of which can happen while the element
is off screen — so gating them only added anIntersectionObserver
subscription plus a tear-down / re-seed cycle on every scroll in and out, for
no benefit. Ungated, they seed their values once at bind time (so avar()
resolves immediately, even for an element below the fold) and never tear down.
Continuously-sampling element sources (size,pointer-local,media,
img-color,video-color) stay gated, where pausing off screen is a real
saving. Additive: off-screen elements that previously had no props until they
scrolled in now carry their seeded values from the start.
Fixed
field-stateno longer loses its latched interaction history when its
element scrolls off screen and back. Because it was viewport-gated,
scrolling the field out of view tore the source down and scrolling back
re-ran it — re-snapshotting each field's "initial" value from the current
(already-edited) value and clearing the dirty / touched / changed latches, so
a field would report pristine again. Now that it runs ungated (above), the
snapshot and latches are taken once and persist for the binding's life.- The shared frame loop no longer keeps
requestAnimationFramescheduled when
no flush is owed. Under aliveHzcadence cap a throttled frame rescheduled
unconditionally; it now reschedules only while a continuous sampler is
registered or a flush is still pending, so a one-shot write can't leave rAF
spinning after its work is done. Any running rAF can demote compositor-driven
CSS animations, so the loop is kept as quiet as possible — it idles the moment
nothing needs it (regression test added). writer.forget()no longer leaves an empty entry that makes a queued frame
flush nothing. When a source's disposal raced a same-frame write, the
emptied target lingered in the pending map past theflushfast-path's size
check; it is now removed so the frame is a true no-op.
Performance
- Removed a per-frame array allocation from the loop's sampler dispatch
([...frameFns]→ directSetiteration). Samplers only ever remove
themselves, whichSetiteration handles safely, so the defensive copy was
unnecessary on the one path that runs every frame. motionandorientationnow attach through the shared, ref-counted
onWindowhelper instead of rawwindow.addEventListener, matching every
other window-driven source — one real listener per event type for the page.
v0.7.2
Fixed
fpssource no longer writes--live-fpsevery frame. The exponential
moving average is still sampled per frame, but the value is now written at most
every 250 ms (≤4 Hz). Writing per frame coupled the readout to the frame rate:
each write invalidates--live-fpsfor the whole tree, and on a large reactive
DOM that restyle is heavy enough to lower the frame rate, which changes the
rounded value, which writes again — a feedback loop that spiralled FPS downward
and accumulated restyle/GC work in Firefox until the tab hung and crashed (no
user interaction required). The throttled write breaks the loop; an fps readout
doesn't need to update faster than a few hertz anyway. Values are unchanged
(rounded EMA), only the write cadence is bounded.
v0.7.1
Fixed
visibilitynow tolerates near-1 intersection ratios. The shared
IntersectionObserverrequests callbacks just below full containment, and the
visibilitysource confirms full visibility from the entry geometry instead of
requiring an exactintersectionRatio === 1. This fixes scroll-triggered
reveal examples that could get stuck on Firefox mobile when the browser reports
a ratio fractionally below 1.
v0.7.0
Breaking release (0.x minor): leaner defaults, viewport-aware sources, and a
truly zero-config auto. Migration notes are under each item.
Changed
autois now fully declarative and attaches nothing by default. It no
longer auto-attachesviewport/pointerto:root. Declare every source —
globals included — withdata-props-for, e.g.<html data-props-for="viewport pointer">. Migration: add the globals you relied on to the root<html>.autoloads plugins on demand. The first time adata-props-forkey needs
a plugin, its chunk is dynamically imported and registered, then the binding
attaches — no moreregisterPlugins()/prop-for-that/pluginsimport when
usingauto. Because it lazy-loads via dynamicimport(), loadautoas a
module:<script type="module" src=".../auto.js">. The classic
auto.global.jsdrop-in is removed (it can't resolve lazy chunks from a CDN).
Migration: switch theautoscript tag totype="module"and drop any
registerPlugins()calls that existed only to feedauto.- Element sources are viewport-gated. An
element-scoped source now runs only
while its element is in the viewport (via the sharedIntersectionObserver);
off screen its work is torn down and its last values freeze in place, resuming +
re-seeding on re-entry. Global sources and:rootbindings are never gated; a
source can opt out withgate: false. No API change for consumers. - The frame loop freezes while the tab is hidden (
document.hidden) and stays
fully idle when nothing is changing — fewer strayrequestAnimationFrames on
pages that aren't actively updating. - Shared
ResizeObserver/IntersectionObservernow support multiple subscribers
per element (with last-entry replay to late subscribers), so an element can be
watched by both a source and the gate at once. autono longer caches a failed plugin load permanently — a transient failure
(offline, blocked, flaky CDN) is dropped so a later request for the same key retries.img-colormemoizes its extracted palette per rendered source, so the viewport
gate re-runningstart()on scroll-in reuses the result instead of re-sampling the
canvas when the image hasn't changed.- Documented the auto path's two limits (light-DOM only / no shadow roots; needs a
CDN that serves thedisttree verbatim) and the gate contract for source authors
(start()is re-invoked on every viewport re-entry, so keep it cheap + idempotent).
Added
gate?: booleanonSource— opt an element source out of viewport gating
(defaults on forscope: 'element').visibilitysets itfalse.isRegistered(key)exported fromprop-for-that— whether a source key is
currently registered.
Removed
pointeris no longer a core source — it writes on everypointermove, so
it's now an opt-in plugin alongsidepointer-local. Migration: underauto
it just works (loaded on demand). Imperatively,registerit first:
import { pointer } from 'prop-for-that/plugins'; register(pointer); propsFor(['pointer']).dist/auto.global.js(the classic, non-moduleautodrop-in) — see the
autonote above.
v0.6.4
Added
nav-typeplugin —--const-nav-type: how the user arrived at the page,
written once as a string (navigate/reload/back_forward/prerender)
fromperformance.getEntriesByType('navigation')[0].type. Pairs with style
queries —@container style(--const-nav-type: reload) { … }. Global scope,
constcadence; typed mode registers it as a<custom-ident>. Import as
navTypefromprop-for-that/plugins. (Rides the batched writer, so it lands
the frame after bind — read the same value in a<head>script if you need it
before first paint.)