kuinetic 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -10
- package/dist/esm/advanced/index.mjs +4727 -0
- package/dist/esm/chunk-AEQGC2KM.mjs +189 -0
- package/dist/esm/chunk-MQSNMYRP.mjs +9332 -0
- package/dist/esm/chunk-RVTYN6NZ.mjs +3446 -0
- package/dist/esm/chunk-U43BX5O2.mjs +2984 -0
- package/dist/esm/core/index.mjs +16 -9
- package/dist/esm/effects/index.mjs +19 -4
- package/dist/esm/index.mjs +17 -10
- package/dist/kuinetic.advanced.js +5070 -0
- package/dist/kuinetic.advanced.min.js +749 -0
- package/dist/kuinetic.all.js +15218 -5595
- package/dist/kuinetic.all.min.js +2 -0
- package/dist/kuinetic.css +2900 -360
- package/dist/kuinetic.js +15217 -5593
- package/dist/kuinetic.min.css +1 -0
- package/dist/kuinetic.min.js +7 -0
- package/dist/types/3d/angles.d.ts +30 -0
- package/dist/types/3d/flattening.d.ts +49 -0
- package/dist/types/3d/index.d.ts +36 -0
- package/dist/types/3d/model-3d.d.ts +115 -0
- package/dist/types/3d/register.d.ts +26 -0
- package/dist/types/3d/webgl.d.ts +24 -0
- package/dist/types/advanced/audio.d.ts +322 -0
- package/dist/types/advanced/base.d.ts +233 -0
- package/dist/types/advanced/camera-3d.d.ts +236 -0
- package/dist/types/advanced/fluid-cursor.d.ts +149 -0
- package/dist/types/advanced/gl-utils.d.ts +52 -0
- package/dist/types/advanced/glsl.d.ts +60 -0
- package/dist/types/advanced/index.d.ts +23 -0
- package/dist/types/advanced/particles.d.ts +108 -0
- package/dist/types/advanced/scenes.d.ts +207 -0
- package/dist/types/advanced/shaders.d.ts +972 -0
- package/dist/types/browser/boot.d.ts +72 -0
- package/dist/types/core/activation.d.ts +227 -2
- package/dist/types/core/animator.d.ts +446 -11
- package/dist/types/core/attrs.d.ts +9 -0
- package/dist/types/core/breakpoints.d.ts +260 -0
- package/dist/types/core/bundles.d.ts +71 -0
- package/dist/types/core/callback.d.ts +64 -0
- package/dist/types/core/capabilities.d.ts +47 -0
- package/dist/types/core/channels.d.ts +120 -2
- package/dist/types/core/cloak-selectors.d.ts +47 -0
- package/dist/types/core/compile.d.ts +207 -2
- package/dist/types/core/control.d.ts +218 -0
- package/dist/types/core/declarations.d.ts +128 -0
- package/dist/types/core/derived/aggregate.d.ts +43 -0
- package/dist/types/core/derived/book.d.ts +30 -0
- package/dist/types/core/derived/diagnostics.d.ts +51 -0
- package/dist/types/core/derived/group-gate.d.ts +62 -0
- package/dist/types/core/derived/install.d.ts +110 -0
- package/dist/types/core/derived/late-matches.d.ts +19 -0
- package/dist/types/core/derived/types.d.ts +108 -0
- package/dist/types/core/easing.d.ts +57 -0
- package/dist/types/core/element-config.d.ts +47 -6
- package/dist/types/core/element-size.d.ts +23 -0
- package/dist/types/core/event-sources.d.ts +39 -0
- package/dist/types/core/flip.d.ts +34 -1
- package/dist/types/core/gesture.d.ts +12 -0
- package/dist/types/core/host-facts.d.ts +155 -0
- package/dist/types/core/index.d.ts +7 -3
- package/dist/types/core/owned-styles.d.ts +129 -2
- package/dist/types/core/params.d.ts +48 -0
- package/dist/types/core/parse.d.ts +28 -1
- package/dist/types/core/register-into.d.ts +29 -0
- package/dist/types/core/registry.d.ts +10 -3
- package/dist/types/core/repeat.d.ts +91 -0
- package/dist/types/core/sequence.d.ts +126 -0
- package/dist/types/core/stagger-config.d.ts +199 -0
- package/dist/types/core/stagger-keys.d.ts +63 -0
- package/dist/types/core/stagger.d.ts +187 -6
- package/dist/types/core/style-plan.d.ts +21 -16
- package/dist/types/core/target.d.ts +128 -0
- package/dist/types/core/threshold-reachability.d.ts +45 -0
- package/dist/types/core/time-scale.d.ts +9 -0
- package/dist/types/core/toggle-actions.d.ts +141 -0
- package/dist/types/core/travel.d.ts +59 -0
- package/dist/types/core/types.d.ts +705 -13
- package/dist/types/core/unquoted-selectors.d.ts +53 -0
- package/dist/types/effects/carousel/drag.d.ts +80 -0
- package/dist/types/effects/carousel/index.d.ts +149 -0
- package/dist/types/effects/catalog/background-media.d.ts +152 -0
- package/dist/types/effects/catalog/discrete.d.ts +13 -0
- package/dist/types/effects/catalog/interaction-proximity.d.ts +24 -0
- package/dist/types/effects/catalog/interaction-reveal.d.ts +248 -0
- package/dist/types/effects/catalog/interaction-shared.d.ts +42 -0
- package/dist/types/effects/catalog/interaction-states.d.ts +68 -0
- package/dist/types/effects/catalog/materials.d.ts +67 -0
- package/dist/types/effects/catalog/media-shared.d.ts +3 -2
- package/dist/types/effects/catalog/numbers-shared.d.ts +8 -5
- package/dist/types/effects/catalog/shared.d.ts +2 -2
- package/dist/types/effects/catalog/text-shared.d.ts +92 -10
- package/dist/types/effects/catalog/transforms.d.ts +13 -0
- package/dist/types/effects/catalog/view-transitions.d.ts +13 -0
- package/dist/types/effects/forms/primitives.d.ts +15 -4
- package/dist/types/effects/gestures/index.d.ts +53 -0
- package/dist/types/effects/index.d.ts +5 -0
- package/dist/types/effects/layout/presets.d.ts +35 -0
- package/dist/types/effects/motion-path/index.d.ts +35 -0
- package/dist/types/effects/scroll-mechanics/params.d.ts +23 -0
- package/dist/types/effects/scroll-mechanics/presets.d.ts +51 -0
- package/dist/types/effects/scroll-mechanics/section-index.d.ts +47 -0
- package/dist/types/effects/shared.d.ts +162 -2
- package/dist/types/effects/step-index.d.ts +121 -0
- package/dist/types/effects/step-marking.d.ts +41 -33
- package/dist/types/effects/svg/icon-parts.d.ts +23 -0
- package/dist/types/effects/three-d/flip-parts.d.ts +46 -0
- package/dist/types/effects/tween/index.d.ts +57 -0
- package/dist/types/effects/tween/properties.d.ts +107 -0
- package/dist/types/effects/tween/waypoints.d.ts +106 -0
- package/dist/types/showcase/compare.d.ts +4 -0
- package/dist/types/showcase/device-frame.d.ts +3 -0
- package/dist/types/showcase/hotspots.d.ts +3 -0
- package/dist/types/showcase/index.d.ts +30 -0
- package/dist/types/showcase/lightbox.d.ts +3 -0
- package/dist/types/showcase/media-source.d.ts +13 -0
- package/dist/types/showcase/modal-shell.d.ts +22 -0
- package/dist/types/showcase/scroll-story.d.ts +4 -0
- package/dist/types/showcase/shared.d.ts +47 -0
- package/dist/types/showcase/slideshow.d.ts +3 -0
- package/dist/types/showcase/slow-mo.d.ts +5 -0
- package/package.json +14 -7
- package/dist/esm/chunk-5DON3UFQ.mjs +0 -4380
- package/dist/esm/chunk-JT4PZL3A.mjs +0 -773
- package/dist/esm/chunk-QUVFODSQ.mjs +0 -1278
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
|
+
import type { Registry } from '../../core/registry.js';
|
|
3
|
+
export declare const TRANSFORMS_PRIMITIVES: Primitive[];
|
|
4
|
+
export declare const TRANSFORMS_PRESETS: Preset[];
|
|
5
|
+
/**
|
|
6
|
+
* Register catalog section R (static transforms) into a registry.
|
|
7
|
+
*
|
|
8
|
+
* @param registry - Registry to populate.
|
|
9
|
+
* @returns The same registry, for chaining.
|
|
10
|
+
* @complexity O(n) time in registered primitives and presets; O(1) extra space.
|
|
11
|
+
* @overallScore 100
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerTransforms(registry: Registry): Registry;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Registry } from '../../core/registry.js';
|
|
2
|
+
import type { Preset, Primitive } from '../../core/types.js';
|
|
3
|
+
export declare const VIEW_TRANSITION_PRIMITIVES: Primitive[];
|
|
4
|
+
export declare const VIEW_TRANSITION_PRESETS: Preset[];
|
|
5
|
+
/**
|
|
6
|
+
* Register the View Transitions family (catalog section L).
|
|
7
|
+
*
|
|
8
|
+
* @param registry - Registry to populate.
|
|
9
|
+
* @returns The same registry, for chaining.
|
|
10
|
+
* @complexity O(n) time in registered primitives and presets.
|
|
11
|
+
* @overallScore 100
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerViewTransitions(registry: Registry): Registry;
|
|
@@ -15,13 +15,24 @@ export declare const VALIDATE_CHECK_PRIMITIVE: Primitive;
|
|
|
15
15
|
export declare function computeStrength(value: string): number;
|
|
16
16
|
export declare const STRENGTH_METER_PRIMITIVE: Primitive;
|
|
17
17
|
export declare const RANGE_FILL_PRIMITIVE: Primitive;
|
|
18
|
+
export { nextStep, prevStep, clampStep, countSteps, delegateControls } from '../step-index.js';
|
|
19
|
+
export type { ControlGroup } from '../step-index.js';
|
|
18
20
|
/**
|
|
19
|
-
*
|
|
21
|
+
* The only member of the family that is not `reducedMotion: 'disable'`, and the difference is the
|
|
22
|
+
* whole widget.
|
|
20
23
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
24
|
+
* Under `disable` the animator never activates the instance, so the deferred setup never runs: no
|
|
25
|
+
* control listeners, no index published, no slide marked. A visitor who asked for reduced motion
|
|
26
|
+
* got a deck whose arrows and dots did nothing at all — not a calmer carousel, a broken one.
|
|
27
|
+
* Reduced motion is a request to suppress animation, never to withdraw function.
|
|
28
|
+
*
|
|
29
|
+
* Nothing is lost by shortening instead, because this primitive animates nothing itself. It writes
|
|
30
|
+
* `data-kui-step` / `--kui-step` on the container and `data-kui-step-state` / `--kui-offset` on the
|
|
31
|
+
* steps; every transition or animation keyed off those belongs to the page or to forms.css, which
|
|
32
|
+
* is exactly the layer base.css's `[data-kui-rm]` block reaches — including, since this changed,
|
|
33
|
+
* the shipped stepper segments' own 200ms fade. The index still moves and the right slide is still
|
|
34
|
+
* marked; the travel between them is what gets taken away.
|
|
23
35
|
*/
|
|
24
|
-
export declare function nextStep(step: number, total: number): number;
|
|
25
36
|
export declare const STEP_PROGRESS_PRIMITIVE: Primitive;
|
|
26
37
|
export type SubmitStage = 'idle' | 'loading' | 'done';
|
|
27
38
|
/**
|
|
@@ -5,6 +5,59 @@ import type { Registry } from '../../core/registry.js';
|
|
|
5
5
|
*
|
|
6
6
|
* The drag family differs only in where a release goes: back to origin (elastic), onward with
|
|
7
7
|
* momentum (throwable), or nowhere (plain drag). That is one primitive with two booleans.
|
|
8
|
+
*
|
|
9
|
+
* All thirteen declare `phase: 'state'`, and none declares `transitions` (nothing here renders
|
|
10
|
+
* through CSS transitions to derive it from — every primitive in this file is `renderer:
|
|
11
|
+
* 'javascript'`), so this is the whole story rather than a shortcut around it. `compile.ts`'s
|
|
12
|
+
* `phaseOf` treats an undeclared phase as a fact nothing is known about, which conflicts with
|
|
13
|
+
* everything including itself — before this, `data-kui="fade-up on:enter, drag"` hit that wall:
|
|
14
|
+
* both segments claim `translate`, `draggable`'s renderer disqualifies the additive rescue (only
|
|
15
|
+
* `css-keyframes` tracks qualify), no combo is registered for the pair, and the compiler silently
|
|
16
|
+
* kept only `fade-up` — an entrance plus a drag on one element, an entirely ordinary request,
|
|
17
|
+
* did nothing. Interaction-reveal's four presets (`masked-label-swap*`, `hover-intent`) shipped
|
|
18
|
+
* the identical gap hours earlier; this is the same bug in its second family, not a new one.
|
|
19
|
+
*
|
|
20
|
+
* The test throughout is *when does this effect hold the channel it claims*, not *is this a
|
|
21
|
+
* press*: `'state'` means "governed by a condition the visitor changes, released — or resting at
|
|
22
|
+
* a value it holds until changed again — once that condition is gone," which is exactly
|
|
23
|
+
* `entrance`'s counterpart (an entrance plays once and hands the channel to whatever the cascade
|
|
24
|
+
* says next; see `EffectPhase`'s doc comment). Every one of these primitives writes an inline
|
|
25
|
+
* style or attribute imperatively rather than through a CSS animation track, so there is no
|
|
26
|
+
* `to`-only or `from`-only keyframe to inspect — the fact instead is about *when the gesture
|
|
27
|
+
* runs*:
|
|
28
|
+
*
|
|
29
|
+
* - `draggable` (`drag`, `drag-x`, `drag-y`, `drag-inertia`, `throwable`, `elastic-pull`,
|
|
30
|
+
* `rubber-band`, `snap-back`) writes `translate` only from `pointerdown` through however long
|
|
31
|
+
* the release takes to resolve — instantly for the plain three, a spring settle for the
|
|
32
|
+
* inertia pair, a spring return to zero for the elastic three (see `primitives.ts`'s `settle`).
|
|
33
|
+
* The inertia pair is the one worth pausing on, because it keeps moving *after* the pointer
|
|
34
|
+
* lifts: `drag-inertia`/`throwable` are not "done interacting" the instant `onEnd` fires, but
|
|
35
|
+
* they are also not `idle` — `idle` in this catalog means an unbounded loop that never yields
|
|
36
|
+
* the channel (`--kui-fx-<name>-iterations: infinite`), and a decelerating throw is bounded and
|
|
37
|
+
* does stop. It is the same one continuous gesture instance settling on its own terms, not a
|
|
38
|
+
* second trigger, so it is still `state` and not a fourth thing this enum has no room for.
|
|
39
|
+
* Every member rests at whatever `translate` it ends on — dropped in place for the plain three,
|
|
40
|
+
* carried by momentum for the inertia pair, snapped to zero for the elastic three — and holds
|
|
41
|
+
* there via the same inline style until the next drag picks it up again. None of that is an
|
|
42
|
+
* `entrance`: there is no hidden from-state and no `cloak`, just an idle rest value between
|
|
43
|
+
* gestures.
|
|
44
|
+
* - `swipeable` (`swipe`, `swipe-x`) and `pressable` (`long-press`) claim the primitive-level
|
|
45
|
+
* `'state'` *channel* (a bucket for "writes an attribute, not a CSS property" — see
|
|
46
|
+
* `gestures/primitives.ts`'s `channels: ['state']` — a name that collides with this same-named
|
|
47
|
+
* `EffectPhase` value only in spelling, not in meaning). `long-press` is the clean case: it
|
|
48
|
+
* flips `data-kui-pressed` on the hold and back off on release, precisely the `:active`-shaped
|
|
49
|
+
* "held only while the visitor is doing something" case the `state` phase doc describes.
|
|
50
|
+
* `swipe`/`swipe-x` are a discrete, already-over gesture by the time `data-kui-swipe` is
|
|
51
|
+
* written (`onSwipe` fires once, at recognition) rather than a continuous hold — but the same
|
|
52
|
+
* family precedent already covers a discrete, dwell-triggered write: `hover-intent` (
|
|
53
|
+
* `interaction-reveal.ts`) sets its class once, on a timer firing during a hover, and keeps it
|
|
54
|
+
* until the pointer actually leaves, and that shipped `phase: 'state'`. A swipe's attribute is
|
|
55
|
+
* the same shape — written once by an interaction, held until the same primitive changes it
|
|
56
|
+
* again — and not an `entrance`, `exit`, or unbounded `idle`.
|
|
57
|
+
* - `magnetic` (`magnetic`, `magnetic-snap`) writes `translate` continuously while the pointer is
|
|
58
|
+
* within `radius`, and springs back toward zero the moment it leaves — proximity rather than a
|
|
59
|
+
* press, but still a condition the visitor's own movement drives and un-drives, the same
|
|
60
|
+
* `state` shape as a `:hover` rule with a transition.
|
|
8
61
|
*/
|
|
9
62
|
export declare const GESTURE_PRESETS: Preset[];
|
|
10
63
|
/**
|
|
@@ -2,10 +2,15 @@ import { Registry } from '../core/registry.js';
|
|
|
2
2
|
export { PRIMITIVES, PRESETS, COMBOS, registerCore } from './catalog/core.js';
|
|
3
3
|
export { registerGestures } from './gestures/index.js';
|
|
4
4
|
export { registerLayout } from './layout/index.js';
|
|
5
|
+
export { registerMotionPath } from './motion-path/index.js';
|
|
5
6
|
export { registerScrollMechanics } from './scroll-mechanics/index.js';
|
|
6
7
|
export { registerSvg } from './svg/index.js';
|
|
7
8
|
export { registerThreeD } from './three-d/index.js';
|
|
9
|
+
export { registerCarousel } from './carousel/index.js';
|
|
8
10
|
export { registerCatalog } from './catalog/index.js';
|
|
11
|
+
export { registerTween, TWEEN_PRESETS, TWEEN_PRIMITIVES } from './tween/index.js';
|
|
12
|
+
export { registerViewTransitions } from './catalog/view-transitions.js';
|
|
13
|
+
export { registerShowcase } from '../showcase/index.js';
|
|
9
14
|
/**
|
|
10
15
|
* A registry with the full catalog registered.
|
|
11
16
|
*
|
|
@@ -5,5 +5,40 @@ import type { Preset } from '../../core/types.js';
|
|
|
5
5
|
* The four `flip-*` names are the same primitive: what differs is why the children moved, and the
|
|
6
6
|
* engine neither knows nor needs to. They exist as separate names because authors think in terms
|
|
7
7
|
* of "I filtered a list", not "I mutated a child list".
|
|
8
|
+
*
|
|
9
|
+
* Phase (see `EffectPhase`, `core/channels.ts`): eight of these nine are deliberately left
|
|
10
|
+
* unphased, and that was checked against the actual mechanism rather than assumed from "FLIP looks
|
|
11
|
+
* like an entrance."
|
|
12
|
+
*
|
|
13
|
+
* `flip-container` (the seven `flip-*`/`grid-to-list`/`masonry-reflow`/`expand-to-modal` names) and
|
|
14
|
+
* `auto-height` genuinely *do* release the channels they animate: `flip.ts`'s `runDeltas` and
|
|
15
|
+
* `primitives.ts`'s `animateHeight` both compile `fill: delay > 0 ? 'backwards' : 'none'` — never
|
|
16
|
+
* `'forwards'`/`'both'` — so once a reorder or a height tween finishes, the Web Animation's
|
|
17
|
+
* contribution is gone and the element's `translate`/`scale`/`height` reads whatever the DOM's own
|
|
18
|
+
* layout or the page's own stylesheet says next. Nothing here holds a rest value the way
|
|
19
|
+
* `gestures.ts`'s `draggable` does. That is exactly the `entrance` shape ("plays once from a
|
|
20
|
+
* displaced from-state, then yields the channel") — but it cannot be declared `entrance`, because
|
|
21
|
+
* that value's contract in this catalog is narrower than the English word: `test
|
|
22
|
+
* /composition-phase.test.ts` asserts, catalog-wide, that every `phase: 'entrance'` preset backs
|
|
23
|
+
* onto a real `@keyframes` block with no closing `to`/`100%` step, to prove the release is real.
|
|
24
|
+
* These primitives are `renderer: 'javascript'` and animate through ad hoc `Element.animate()`
|
|
25
|
+
* calls built at runtime — there is no `@keyframes kui-flip-reorder` (or `-accordion-height`) in
|
|
26
|
+
* any shipped stylesheet for that test to find, so declaring `entrance` here would fail an
|
|
27
|
+
* off-limits invariant rather than extend it. None of the other three values fit either: not
|
|
28
|
+
* `idle` (every run is bounded — `deltas.length === 0` even short-circuits to a no-op), not `state`
|
|
29
|
+
* (nothing is held between calls to persist), not `exit` (nothing departs; a reorder settles back
|
|
30
|
+
* into a normal, still-visible layout position). Left unphased, these eight keep conflicting with
|
|
31
|
+
* a same-channel neighbour exactly as they did before this change — a real gap, but the one the
|
|
32
|
+
* mechanism actually supports today rather than a value chosen to make the enum feel complete.
|
|
33
|
+
*
|
|
34
|
+
* `tab-indicator-slide` (`flip-indicator`) is the one exception, and the reason is a real
|
|
35
|
+
* difference in the primitive, not a different reading of the same one. `prepareIndicator` writes
|
|
36
|
+
* the indicator's landing spot with `ctx.style.set('translate', ...)` — a **persistent** inline
|
|
37
|
+
* style — before it ever calls `engine.play()`; the FLIP animation only smooths the visible trip
|
|
38
|
+
* between the old inline value and the new one, and hands back to that same persisted inline style
|
|
39
|
+
* on finish (fill: none resolves to identity, which is "no offset from the inline value already
|
|
40
|
+
* written"). So the rest state genuinely is held, between moves, by an inline write the same way
|
|
41
|
+
* `draggable`'s drop position is — the exact `state` shape `gestures/index.ts` established, just
|
|
42
|
+
* driven by "the followed target changed" instead of a pointer.
|
|
8
43
|
*/
|
|
9
44
|
export declare const LAYOUT_PRESETS: Preset[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
|
+
import type { Registry } from '../../core/registry.js';
|
|
3
|
+
/**
|
|
4
|
+
* The one primitive. `perfClass: 'compositor'` is the shared default and is honest here:
|
|
5
|
+
* `offset-distance` is a transform-like property that Chromium animates off the main thread.
|
|
6
|
+
*
|
|
7
|
+
* `reducedMotion` is left at `cssPrimitive`'s `'shorten'`, which resolves to a 1ms animation — the
|
|
8
|
+
* element arrives at the end of its path with no motion to see, which is the right answer for a
|
|
9
|
+
* vestibular trigger and the same treatment `slide-out-up` and every other travel-and-stay effect
|
|
10
|
+
* already gets. `'disable'` would be worse, not better: it leaves the animation off entirely, so
|
|
11
|
+
* an entrance like `path-swoop` would sit permanently at its *start* — off to the lower left of
|
|
12
|
+
* where it belongs. `base.css` additionally drops `offset-path` under `disable` and in print, so
|
|
13
|
+
* a composed effect that forces that policy cannot strand a path-driven element off its mark.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MOTION_PATH_PRIMITIVES: Primitive[];
|
|
16
|
+
/**
|
|
17
|
+
* The named paths.
|
|
18
|
+
*
|
|
19
|
+
* §6.4 of the parity outline makes shipping these non-negotiable, and it is right to: a primitive
|
|
20
|
+
* that only works once the author has opened a vector editor is not a catalog entry, it is an API.
|
|
21
|
+
* Every name below animates on its own, with nothing but `data-kui="path-arc"`.
|
|
22
|
+
*
|
|
23
|
+
* The geometry is written to read at a glance — round numbers, control points that are obviously
|
|
24
|
+
* symmetrical — because these are the first thing anyone will copy and edit.
|
|
25
|
+
*/
|
|
26
|
+
export declare const MOTION_PATH_PRESETS: Preset[];
|
|
27
|
+
/**
|
|
28
|
+
* Register the motion-path family.
|
|
29
|
+
*
|
|
30
|
+
* @param registry - Registry to populate.
|
|
31
|
+
* @returns The same registry, for chaining.
|
|
32
|
+
* @complexity O(n) time in the number of primitives and presets.
|
|
33
|
+
* @overallScore 100
|
|
34
|
+
*/
|
|
35
|
+
export declare function registerMotionPath(registry: Registry): Registry;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ParameterSchema } from '../../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The parameter schemas shared across the scroll-mechanics primitives.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `primitives.ts`, which sits close enough to the 400-line cap that adding one
|
|
6
|
+
* parameter pushes it over. They belong together anyway: these are the knobs more than one
|
|
7
|
+
* primitive declares, and the whole point of declaring them once is that `pin` and `media-scrub`
|
|
8
|
+
* cannot drift apart on what a hold means.
|
|
9
|
+
*/
|
|
10
|
+
export declare const distanceParam: ParameterSchema;
|
|
11
|
+
/**
|
|
12
|
+
* Shared by every primitive that has to hold still while its scroll range passes.
|
|
13
|
+
*
|
|
14
|
+
* `pin` and `media-scrub` were solving the same problem twice from opposite ends. The pin declared
|
|
15
|
+
* both of these and did the work; the scrub declared neither and left it to the page, which is why
|
|
16
|
+
* `demo/scroll.html` carried a `.scrub-stage` whose entire job was `height: 260vh` and a
|
|
17
|
+
* `.scrub-viewport` whose entire job was `position: sticky`. Two hand-written boxes to restate
|
|
18
|
+
* something the library already knew, because `distance:` was on the attribute the whole time.
|
|
19
|
+
*
|
|
20
|
+
* Declaring it once means the two cannot drift, and it is what lets a scrub become one attribute
|
|
21
|
+
* with no wrapper at all.
|
|
22
|
+
*/
|
|
23
|
+
export declare const stickyParams: ParameterSchema;
|
|
@@ -2,5 +2,56 @@ import type { Preset } from '../../core/types.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Scroll-mechanics names. Eleven names over six primitives — the same alias-table shape the
|
|
4
4
|
* entrance matrix uses, so adding a variant stays a data change.
|
|
5
|
+
*
|
|
6
|
+
* **Every name here declares `phase: 'idle'`.** This is the family the phase census flagged as
|
|
7
|
+
* hardest, because "when does this hold the channel" does not obviously map onto a visitor
|
|
8
|
+
* condition or a one-shot arrival the way an entrance or a gesture does — a scrub is driven by
|
|
9
|
+
* scroll position, which is continuous, not a boolean the way `:hover` or `[aria-expanded]` is.
|
|
10
|
+
* The question worth separating is *whether it changes* (yes, every frame) from *whether it ever
|
|
11
|
+
* hands the channel back* (no, never, for as long as the instance lives), and it is the second one
|
|
12
|
+
* `EffectPhase` actually asks.
|
|
13
|
+
*
|
|
14
|
+
* Every primitive in this file writes its claimed channel(s) **unconditionally from the moment it
|
|
15
|
+
* activates** — `preparePin`'s `installSticky` sets `position: sticky` inline the instant `prepare`
|
|
16
|
+
* runs, not the instant the element is actually in range; `prepareHorizontal`/`prepareMediaScrub`/
|
|
17
|
+
* `prepareProgress`/`prepareScrollSpy` all start writing on the very first scroll frame, which for a
|
|
18
|
+
* `load`-activated effect (their shared `defaultActivation`) is essentially immediately — and never
|
|
19
|
+
* releases it until `destroy()` tears the instance down. That is a structurally different shape from
|
|
20
|
+
* `state`: a `:hover`/drag/press effect writes *nothing at all* until the visitor's own action
|
|
21
|
+
* starts it, which is what lets an entrance's one-shot fill resolve first and hand the channel to a
|
|
22
|
+
* quiescent neighbour that has not spoken yet (`gestures/index.ts`'s `draggable` family is exactly
|
|
23
|
+
* this — silent until `pointerdown`). A scroll effect never has that silent stretch: it is live from
|
|
24
|
+
* the same instant an entrance would be trying to release into it, so treating them as taking turns
|
|
25
|
+
* (`state`'s exemption) would be describing a truce that doesn't exist. "Runs unbounded, so it never
|
|
26
|
+
* yields the channel at all" is `EffectPhase`'s own definition of `idle`, and `EffectInstance
|
|
27
|
+
* .continuous`'s doc (`core/types.ts`) already names this exact set — "a pin, a scroll progress
|
|
28
|
+
* track, a media scrub" — as the library's canonical unbounded, no-end effects. `idle` is that fact
|
|
29
|
+
* restated as a composition phase, not a new claim.
|
|
30
|
+
*
|
|
31
|
+
* `scroll-spy` earns the same label by the same test even though it writes an *attribute* rather
|
|
32
|
+
* than a style: `data-kui-active` moves between links continuously as sections cross the threshold,
|
|
33
|
+
* with no moment where nothing is marked active and the channel is free.
|
|
34
|
+
*
|
|
35
|
+
* `smooth-scroll-to` and `scroll-snap-x`/`-y` are not wrapped in `continuousSetup` — they set one
|
|
36
|
+
* declaration at `prepare` and are done, so the animator considers the *instance* finished
|
|
37
|
+
* immediately. That is orthogonal to the question here: the declaration they wrote
|
|
38
|
+
* (`scroll-behavior`, `scroll-snap-type`) stays applied inline for the instance's whole life exactly
|
|
39
|
+
* like the continuous ones, never conditionally, never released early. A permanently-held static
|
|
40
|
+
* write and a per-frame recomputed one both fail `state`'s test the same way.
|
|
41
|
+
*
|
|
42
|
+
* Declaring `idle` does not, in itself, open any new composition today — `INDEPENDENT_PHASES` in
|
|
43
|
+
* `core/channels.ts` only exempts `entrance|state` and `exit|state`, and `idle` pairs with nothing,
|
|
44
|
+
* itself included, which is the same outcome an undeclared phase already produced. What it changes
|
|
45
|
+
* is the meaning of that outcome: these thirteen collide with a channel-sharing neighbour because
|
|
46
|
+
* two effects are provably fighting over the same property at the same time (checked directly —
|
|
47
|
+
* `horizontal-scroll` and any `translate`-writing entrance both actively write `translate` from the
|
|
48
|
+
* moment the entrance would want to release, so there is no daylight to compose into), not because
|
|
49
|
+
* nothing was known about them. `test/catalog-phase-mechanics.test.ts` asserts both halves of that:
|
|
50
|
+
* the declaration is present, and a same-channel pair inside this family still correctly refuses.
|
|
51
|
+
*
|
|
52
|
+
* None of the eleven declares `transitions` — `phaseOf` (`core/compile.ts`) only ever *derives*
|
|
53
|
+
* `state` from that field, so a preset carrying both would be duplicate, driftable data, and every
|
|
54
|
+
* primitive here is `renderer: 'javascript'` with nothing rendered through a CSS `transition:` to
|
|
55
|
+
* begin with.
|
|
5
56
|
*/
|
|
6
57
|
export declare const SCROLL_PRESETS: Preset[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { PrepareContext } from '../../core/effect-context.js';
|
|
2
|
+
import type { ScrollFrame } from '../../core/scroll-scheduler.js';
|
|
3
|
+
import type { Cleanup } from '../../core/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options for tracking the active section index as the scroller advances.
|
|
6
|
+
*/
|
|
7
|
+
export interface SectionIndexOptions {
|
|
8
|
+
el: Element;
|
|
9
|
+
sections: Element[];
|
|
10
|
+
ctx: PrepareContext;
|
|
11
|
+
offsetTop?: string;
|
|
12
|
+
onChange: (next: number, previous: number) => void;
|
|
13
|
+
}
|
|
14
|
+
export type SectionIndex = Cleanup & {
|
|
15
|
+
release: () => void;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* `offset-top` resolved to pixels for one frame.
|
|
19
|
+
*
|
|
20
|
+
* Evaluated statically per frame using the scheduler's viewport metrics so viewport units
|
|
21
|
+
* like `50vh` update with resize.
|
|
22
|
+
*
|
|
23
|
+
* @complexity O(n) time in the authored value's length; O(1) space.
|
|
24
|
+
* @overallScore 100
|
|
25
|
+
*/
|
|
26
|
+
export declare function offsetTopPixels(authored: string, frame: ScrollFrame): number;
|
|
27
|
+
/**
|
|
28
|
+
* The highest index whose content-relative top has reached the reference line.
|
|
29
|
+
*
|
|
30
|
+
* `-1` when none has — before the first section, or with no sections at all. Callers rely on this
|
|
31
|
+
* being the *only* thing that decides "active": one number, or none, never more than one.
|
|
32
|
+
*
|
|
33
|
+
* @complexity O(n) time in section count; O(1) space.
|
|
34
|
+
* @overallScore 100
|
|
35
|
+
*/
|
|
36
|
+
export declare function highestReachedIndex(tops: number[], scrollTop: number, line: number): number;
|
|
37
|
+
/**
|
|
38
|
+
* Measure section tops once per resize epoch and report the highest reached section index on change.
|
|
39
|
+
*
|
|
40
|
+
* Shared measuring and scroll subscription half extracted from `prepareScrollSpyContainer`,
|
|
41
|
+
* reused by `scroll-spy` and `scroll-story`.
|
|
42
|
+
*
|
|
43
|
+
* @complexity O(n) time and space in section count, recomputed every frame — cheap arithmetic
|
|
44
|
+
* against a rect cache, never a fresh layout read once per resize epoch has passed.
|
|
45
|
+
* @overallScore 100
|
|
46
|
+
*/
|
|
47
|
+
export declare function createSectionIndex(options: SectionIndexOptions): SectionIndex;
|
|
@@ -1,8 +1,168 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PrepareContext } from '../core/effect-context.js';
|
|
2
|
+
import type { Activation, EffectParams, NamedActivation, ParameterSchema, Primitive, ReducedMotionPolicy, Timeline } from '../core/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The one `delay` declaration, spread by every primitive that waits for a trigger before playing.
|
|
5
|
+
*
|
|
6
|
+
* Two spellings of an authored delay exist and they arrive by different routes. The positional
|
|
7
|
+
* one (`count-up 320ms 300ms`) rides alongside the parameter record as `params.timing` — see the
|
|
8
|
+
* design note in `core/js-effect-preparer.ts` for why timing is deliberately *not* merged into
|
|
9
|
+
* that record — while the named one (`count-up delay:300ms`) reaches a primitive only if its
|
|
10
|
+
* schema declares `delay`, and is otherwise dropped by `resolveParams` as an unknown parameter.
|
|
11
|
+
* So every primitive whose start moment is a trigger has to declare this, and declaring it is
|
|
12
|
+
* safe precisely because `0ms` is a true no-op: `readEffectParams` pre-fills every declared
|
|
13
|
+
* parameter, so a non-zero default would be indistinguishable from an authored value.
|
|
14
|
+
*
|
|
15
|
+
* Spread (`...TRIGGER_DELAY_PARAM`) rather than retyped so that rationale has one home.
|
|
16
|
+
*
|
|
17
|
+
* "Waits for a trigger" is wider than `defaultActivation: 'enter'`, which is all the structural
|
|
18
|
+
* guard in `test/js-effect-timing.test.ts` originally covered. A hover lift, an `aria-expanded`
|
|
19
|
+
* icon toggle and a FLIP reorder all default to `'load'` — they install listeners or an observer
|
|
20
|
+
* at load — yet each has a perfectly definite start moment later on (the pointer arrives, the
|
|
21
|
+
* attribute flips, the children move), and "start 200ms after that moment" is a coherent request
|
|
22
|
+
* for every one of them. `test/js-effect-timing-parity.test.ts` now guards both groups; its
|
|
23
|
+
* `TIMING_REFUSALS` table names the primitives that genuinely have no start moment and must *not*
|
|
24
|
+
* pick this up.
|
|
25
|
+
*/
|
|
26
|
+
export declare const TRIGGER_DELAY_PARAM: ParameterSchema;
|
|
27
|
+
/** The three positional timing tokens of the grammar: `data-kui="fade-up 600ms 200ms linear"`. */
|
|
28
|
+
export type TimingToken = 'duration' | 'delay' | 'ease';
|
|
29
|
+
/** All three, in grammar order. Also the `honours` list of a primitive that supports the lot. */
|
|
30
|
+
export declare const ALL_TIMING_TOKENS: readonly TimingToken[];
|
|
31
|
+
/**
|
|
32
|
+
* What a JS-rendered primitive can actually *do* with authored timing.
|
|
33
|
+
*
|
|
34
|
+
* The problem this closes: the two spellings of a timing value fail differently, and one of them
|
|
35
|
+
* fails silently. `pin-section delay:300ms` reaches `readParams`, is not in the schema, and warns
|
|
36
|
+
* as an unknown parameter — the author is told. `pin-section 0ms 300ms` parses into
|
|
37
|
+
* `spec.delay`, is lifted out to `params.timing` (see `core/js-effect-preparer.ts`), and is then
|
|
38
|
+
* simply never read by a primitive that has no clock to shift. Nothing warns, nothing happens,
|
|
39
|
+
* and the page gives the author no way to find out which of those two it was.
|
|
40
|
+
*
|
|
41
|
+
* So a primitive that cannot honour a token has to say so, and it has to say so about the
|
|
42
|
+
* positional spelling too. Declaring the contract is how it does that.
|
|
43
|
+
*/
|
|
44
|
+
export interface TimingContract {
|
|
45
|
+
/**
|
|
46
|
+
* Tokens this primitive genuinely acts on. Anything omitted warns by name when authored.
|
|
47
|
+
* Omit the field entirely for a primitive that honours none of the three.
|
|
48
|
+
*/
|
|
49
|
+
honours?: readonly TimingToken[];
|
|
50
|
+
/**
|
|
51
|
+
* Completes `"<id>" cannot honour <token>: <because>`. Write the *reason*, not the symptom —
|
|
52
|
+
* "it tracks pointer position continuously, so there is no start moment to delay" tells an
|
|
53
|
+
* author to stop looking for a spelling that works, where "unsupported" does not.
|
|
54
|
+
*/
|
|
55
|
+
because: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* How long an effect waits after its trigger before it starts, in milliseconds.
|
|
59
|
+
*
|
|
60
|
+
* The named form of the pattern `text.ts` spells out at three call sites —
|
|
61
|
+
* `params.timing.delayMs ?? params.ms('delay', 0)` — and the sibling of
|
|
62
|
+
* `core/js-params.ts`'s `effectDurationMs`. Two spellings, one intent: `split-flap 400ms 200ms`
|
|
63
|
+
* and `split-flap 400ms delay:200ms` mean the same thing, and the positional one is what `play()`
|
|
64
|
+
* emits, so it is the one that must not be the one dropped.
|
|
65
|
+
*
|
|
66
|
+
* It lives here rather than beside `effectDurationMs` only because `src/core/` is being edited in
|
|
67
|
+
* parallel; the two belong together and should be moved once that settles.
|
|
68
|
+
*
|
|
69
|
+
* @param fallback - Used when the author wrote neither spelling. Effectively always `0`.
|
|
70
|
+
* @complexity O(1) time and space.
|
|
71
|
+
* @overallScore 100
|
|
72
|
+
*/
|
|
73
|
+
export declare function effectDelayMs(params: EffectParams, fallback?: number): number;
|
|
74
|
+
/**
|
|
75
|
+
* The curve an effect runs on, preferring the positional token over the same-named parameter.
|
|
76
|
+
*
|
|
77
|
+
* The easing half of the same story. A primitive reading only `params.text('ease')` sees
|
|
78
|
+
* `flip-shuffle ease:linear` and not `flip-shuffle 400ms 0ms linear`, which is the identical
|
|
79
|
+
* request written the way the grammar puts it first.
|
|
80
|
+
*
|
|
81
|
+
* @param fallback - Used when the author wrote neither spelling.
|
|
82
|
+
* @complexity O(1) time and space.
|
|
83
|
+
* @overallScore 100
|
|
84
|
+
*/
|
|
85
|
+
export declare function effectEasing(params: EffectParams, fallback?: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* Copy the honoured positional tokens onto the primitive's own namespaced custom properties.
|
|
88
|
+
*
|
|
89
|
+
* Only the `key:value` spelling reaches CSS on its own: `compile.buildPlan` runs `resolveParams`
|
|
90
|
+
* over `spec.params` for every effect, but `pushTrack` — which is where `spec.duration`/`.delay`/
|
|
91
|
+
* `.easing` are turned into declarations — runs for `css-keyframes` primitives only. So a
|
|
92
|
+
* stylesheet-driven JS primitive sees `lift duration:400ms` and not `lift 400ms`, which is the
|
|
93
|
+
* same value written the other way round.
|
|
94
|
+
*
|
|
95
|
+
* Written through the ledger and therefore restored on teardown, and written *after*
|
|
96
|
+
* `applyStylePlan` has already put any `key:value` overrides inline — so the positional spelling
|
|
97
|
+
* wins a collision, matching `effectDurationMs`'s `params.timing.durationMs ?? params.ms(...)`
|
|
98
|
+
* precedence rather than contradicting it.
|
|
99
|
+
*
|
|
100
|
+
* Takes the whole context rather than the ledger off it so that an unauthored effect never
|
|
101
|
+
* dereferences `ctx` at all. That is not fastidiousness: `test/three-d.test.ts` calls the
|
|
102
|
+
* state-driven presets' `prepare` with no element and no context, asserting that the default path
|
|
103
|
+
* reaches for neither, and a caller like `prepareCardToggle` runs this before it knows whether it
|
|
104
|
+
* has anything else to do.
|
|
105
|
+
*
|
|
106
|
+
* @complexity O(1) time and space — three tokens, fixed.
|
|
107
|
+
* @overallScore 100
|
|
108
|
+
*/
|
|
109
|
+
export declare function mirrorTimingToCss(id: string, honours: readonly TimingToken[], params: EffectParams, ctx: PrepareContext): void;
|
|
110
|
+
/**
|
|
111
|
+
* Wrap a primitive's own `prepare` so authored timing it cannot act on warns instead of vanishing.
|
|
112
|
+
*
|
|
113
|
+
* For primitives that drive their own frames. The wrapped `prepare` still does all the work; this
|
|
114
|
+
* only adds the diagnostics, which is why it composes with `deferPrepare` rather than replacing it.
|
|
115
|
+
*
|
|
116
|
+
* @param id - The primitive's id, named in the warning.
|
|
117
|
+
* @param contract - What this primitive honours, and why it cannot honour the rest.
|
|
118
|
+
* @param prepare - The primitive's real setup.
|
|
119
|
+
* @complexity O(1) time and space beyond the wrapped call.
|
|
120
|
+
* @overallScore 100
|
|
121
|
+
*/
|
|
122
|
+
export declare function withTimingContract(id: string, contract: TimingContract, prepare: NonNullable<Primitive['prepare']>): NonNullable<Primitive['prepare']>;
|
|
123
|
+
/**
|
|
124
|
+
* The `prepare` for a primitive whose motion is a stylesheet transition or keyframe rather than
|
|
125
|
+
* frames this library drives — the hover family, the icon toggles, the flip card.
|
|
126
|
+
*
|
|
127
|
+
* These used to be `prepare: () => inertInstance()`, registered purely so the name parses and
|
|
128
|
+
* `data-kui-fx` gets stamped. That was almost right: the *named* spelling of a timing parameter
|
|
129
|
+
* already reaches their CSS through `resolveParams`, so there was nothing for JS to do. The
|
|
130
|
+
* positional spelling had no route at all, which is the gap this closes — plus the warning for
|
|
131
|
+
* whichever tokens the shipped rule pins (a linear spin's `ease`, say).
|
|
132
|
+
*
|
|
133
|
+
* @param id - The primitive's id; also the namespace of the properties written.
|
|
134
|
+
* @param contract - Which tokens the stylesheet actually reads, and why it ignores the others.
|
|
135
|
+
* @complexity O(1) time and space.
|
|
136
|
+
* @overallScore 100
|
|
137
|
+
*/
|
|
138
|
+
export declare function stylesheetTimingPrepare(id: string, contract: TimingContract): NonNullable<Primitive['prepare']>;
|
|
139
|
+
/**
|
|
140
|
+
* "Accepts every timeline, is driven by none of them."
|
|
141
|
+
*
|
|
142
|
+
* A few primitives have no relationship to progress at all: `background-media` paints a backdrop,
|
|
143
|
+
* and the scroll-mechanics drivers read the scroll position themselves rather than being driven by
|
|
144
|
+
* an `animation-timeline`. None of them ever reads `Timeline`.
|
|
145
|
+
*
|
|
146
|
+
* They still have to *accept* one, because `compile.ts`'s `intersect` narrows a composed element's
|
|
147
|
+
* timeline support to what every primitive on it supports. Declaring the honest answer — `['time']`,
|
|
148
|
+
* or nothing — empties that intersection for `data-kui="background-media src:/hero.mp4, parallax"`
|
|
149
|
+
* with `timeline:view`, `style-plan.ts` then refuses the timeline, and the neighbour's scrub
|
|
150
|
+
* silently degrades to a one-shot. Nothing is wrong with either effect; the intersection is simply
|
|
151
|
+
* the wrong question to ask of a primitive that has no opinion.
|
|
152
|
+
*
|
|
153
|
+
* So the list stays exhaustive and this name is what makes it truthful. Written out inline it reads
|
|
154
|
+
* as a claim to support all four — the same lie in four places, each needing its own paragraph to
|
|
155
|
+
* walk back. Written as `supportedTimelines: TIMELINE_AGNOSTIC` it reads as what it is: an
|
|
156
|
+
* abstention, not a claim.
|
|
157
|
+
*
|
|
158
|
+
* One shared array instance, like `ENTRANCE_TIMELINES` in `catalog/core.ts`. Every consumer
|
|
159
|
+
* (`intersect`, `warnUnsupportedTimeline`) only reads it.
|
|
160
|
+
*/
|
|
161
|
+
export declare const TIMELINE_AGNOSTIC: Timeline[];
|
|
2
162
|
export interface CssPrimitiveOptions {
|
|
3
163
|
parameters?: ParameterSchema;
|
|
4
164
|
timelines?: Timeline[];
|
|
5
|
-
activations?:
|
|
165
|
+
activations?: NamedActivation[];
|
|
6
166
|
defaultActivation?: Activation;
|
|
7
167
|
reducedMotion?: ReducedMotionPolicy;
|
|
8
168
|
perfClass?: Primitive['perfClass'];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Cleanup, EffectParams } from '../core/types.js';
|
|
2
|
+
import type { PrepareContext } from '../core/effect-context.js';
|
|
3
|
+
import type { TargetScope } from '../core/target.js';
|
|
4
|
+
export interface StepIndexOptions {
|
|
5
|
+
el: Element;
|
|
6
|
+
params: EffectParams;
|
|
7
|
+
ctx: PrepareContext;
|
|
8
|
+
scope: TargetScope;
|
|
9
|
+
resolveSteps: () => Iterable<Element>;
|
|
10
|
+
onRender?: (index: number, total: number) => void;
|
|
11
|
+
clickFallback?: boolean;
|
|
12
|
+
name?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface StepIndex {
|
|
15
|
+
goTo(index: number): void;
|
|
16
|
+
next(): void;
|
|
17
|
+
prev(): void;
|
|
18
|
+
current(): number;
|
|
19
|
+
total(): number;
|
|
20
|
+
release(): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Advance a step index, wrapping back to 0 — pure, so the wrap rule is assertable without a DOM.
|
|
24
|
+
*
|
|
25
|
+
* @complexity O(1) time and space.
|
|
26
|
+
* @overallScore 100
|
|
27
|
+
*/
|
|
28
|
+
export declare function nextStep(step: number, total: number): number;
|
|
29
|
+
/**
|
|
30
|
+
* Step backwards, wrapping to the last index — the mirror of `nextStep`, and pure for the same
|
|
31
|
+
* reason: the wrap is the whole of the rule and it is worth asserting without a DOM.
|
|
32
|
+
*
|
|
33
|
+
* `+ total` before the modulo rather than a `< 0` branch: `-1 % 5` is `-1` in JS, not `4`, so the
|
|
34
|
+
* naive mirror of `nextStep` silently produces a negative index that marks nothing and reads as a
|
|
35
|
+
* dead control rather than an error.
|
|
36
|
+
*
|
|
37
|
+
* @complexity O(1) time and space.
|
|
38
|
+
* @overallScore 100
|
|
39
|
+
*/
|
|
40
|
+
export declare function prevStep(step: number, total: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* Clamp an authored or computed index into range, wrapping the same way the steppers do.
|
|
43
|
+
*
|
|
44
|
+
* `jump:` takes its index from a control's position among its siblings, which is trustworthy —
|
|
45
|
+
* but `steps:` is authored separately, so a page with six dots and `steps:4` would otherwise set
|
|
46
|
+
* an index no step element has. Wrapping rather than clamping keeps one rule across all three
|
|
47
|
+
* controls instead of two.
|
|
48
|
+
*
|
|
49
|
+
* @complexity O(1) time and space.
|
|
50
|
+
* @overallScore 100
|
|
51
|
+
*/
|
|
52
|
+
export declare function clampStep(step: number, total: number): number;
|
|
53
|
+
/**
|
|
54
|
+
* How many steps there are, asked freshly on each call rather than fixed at setup.
|
|
55
|
+
*
|
|
56
|
+
* `steps:` used to be required and defaulted to 4, which is the wrong shape for a deck: the number
|
|
57
|
+
* is not a *choice* an author makes, it is a fact about how many slides they wrote, and stating it
|
|
58
|
+
* twice means a sixth slide that silently never gets reached. Counting instead makes `steps:` an
|
|
59
|
+
* override for the case the elements cannot answer — and asking per flip rather than once means a
|
|
60
|
+
* slide added after setup is picked up, matching `createStepMarker`, which already re-resolves per
|
|
61
|
+
* flip for exactly that reason.
|
|
62
|
+
*
|
|
63
|
+
* Counted per *parent*, taking the largest group, not as a flat total. `target:` routinely names
|
|
64
|
+
* two parallel groups — the slides and the dots that track them — and a flat count of a five-slide
|
|
65
|
+
* deck with five dots is ten. Per-parent is also precisely how the marker numbers them, so the
|
|
66
|
+
* count and the marking cannot disagree.
|
|
67
|
+
*
|
|
68
|
+
* Exported for `effects/carousel`, which drives an index over the same shape of markup and must
|
|
69
|
+
* count it the same way. A second implementation of "how many slides are there" is exactly the
|
|
70
|
+
* drift `createStepMarker` was extracted to end for the marking half.
|
|
71
|
+
*
|
|
72
|
+
* @complexity O(n) time and space in the matched elements.
|
|
73
|
+
* @overallScore 100
|
|
74
|
+
*/
|
|
75
|
+
export declare function countSteps(params: EffectParams, resolveSteps: () => Iterable<Element>): number;
|
|
76
|
+
/** One named control group: the selector the author wrote, and what pressing a match does. */
|
|
77
|
+
export interface ControlGroup {
|
|
78
|
+
selector: string;
|
|
79
|
+
/** @param position - The pressed control's place among its own group, in document order. */
|
|
80
|
+
run: (node: Element, position: number) => void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Bind every named control group with a *single* delegated listener on the scope root.
|
|
84
|
+
*
|
|
85
|
+
* One listener per matched node was the obvious form and it had a hole with no warning attached:
|
|
86
|
+
* the nodes are matched once, at prepare time, so a dot rendered afterwards was inert forever. The
|
|
87
|
+
* arrows kept working — they ask `countSteps` for the total on every press, so they reached the new
|
|
88
|
+
* slide fine — which made the failure look like "the dots are broken" rather than "controls are
|
|
89
|
+
* bound once". Delegation removes the setup-time snapshot entirely: a control is whatever matches
|
|
90
|
+
* the selector at the moment of the press, which is the same rule `resolveSteps` and `countSteps`
|
|
91
|
+
* already follow, and it collapses teardown from N removals to one.
|
|
92
|
+
*
|
|
93
|
+
* The root is the *scope* root, not simply the host. Under the page scope `step-progress` has
|
|
94
|
+
* always resolved with, a control may legitimately sit outside the element it drives — arrows in a
|
|
95
|
+
* section header above the deck — and a listener on the host would never see those clicks.
|
|
96
|
+
*
|
|
97
|
+
* Exported for `effects/carousel` alongside `countSteps`: `next:`/`prev:`/`jump:` mean the same
|
|
98
|
+
* thing on a ring as they do on a bar, and every subtlety in the walk below (the `:scope` root, the
|
|
99
|
+
* per-press re-query, the `scope:self` containment) was found once and should not be found again.
|
|
100
|
+
*
|
|
101
|
+
* @returns The teardown for the one listener.
|
|
102
|
+
* @complexity O(g × (m + d)) per click — one scoped query and one ancestor walk per named group,
|
|
103
|
+
* in that group's matches `m` and the pressed node's depth `d`; O(m) space for the largest group.
|
|
104
|
+
* @overallScore 100
|
|
105
|
+
*/
|
|
106
|
+
export declare function delegateControls(request: {
|
|
107
|
+
el: Element;
|
|
108
|
+
ctx: PrepareContext;
|
|
109
|
+
scope: TargetScope;
|
|
110
|
+
groups: ControlGroup[];
|
|
111
|
+
}): Cleanup;
|
|
112
|
+
/**
|
|
113
|
+
* Shared step index for click-driven steppers and showcase slideshows.
|
|
114
|
+
*
|
|
115
|
+
* Re-queries steps on every render so added or removed slides cannot leave stale marks. The
|
|
116
|
+
* optional render hook lets a widget mirror the selected index into ARIA without owning it twice.
|
|
117
|
+
*
|
|
118
|
+
* @complexity O(n) per render in the step and control groups; O(n) marker storage.
|
|
119
|
+
* @overallScore 100
|
|
120
|
+
*/
|
|
121
|
+
export declare function createStepIndex(options: StepIndexOptions): StepIndex;
|