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,128 @@
|
|
|
1
|
+
import type { EffectParams, ParamSpec } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The slice of `PrepareContext` `resolveTarget`/`queryScoped` actually need.
|
|
4
|
+
*
|
|
5
|
+
* A real `PrepareContext` satisfies this structurally — it has both fields and more — so every
|
|
6
|
+
* existing call site (the primitives' own `prepare`, which is handed a full `PrepareContext`)
|
|
7
|
+
* keeps compiling unchanged. Declared narrow rather than importing `PrepareContext` itself so
|
|
8
|
+
* `animator.ts`'s `install` — which resolves a retargeted selector at compile-to-install time,
|
|
9
|
+
* before any primitive's `prepare` has run and therefore before a full `PrepareContext` exists —
|
|
10
|
+
* can build the two fields it actually has (`el.ownerDocument`, a warn callback) instead of
|
|
11
|
+
* fabricating the rest of the interface it does not.
|
|
12
|
+
*/
|
|
13
|
+
export interface TargetContext {
|
|
14
|
+
doc: Document;
|
|
15
|
+
warn(message: string): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* `target:`/`scope:` resolution — one convention shared by every place in the library that lets an
|
|
19
|
+
* author name a selector instead of animating the element the attribute sits on.
|
|
20
|
+
*
|
|
21
|
+
* Lives in `core`, not `effects`, because `core/compile.ts` and `core/animator.ts` need it too:
|
|
22
|
+
* `target:` started as a per-primitive parameter six scroll/forms primitives read for themselves
|
|
23
|
+
* (`effects/step-marking.ts`'s `createStepMarker` still owns the step-index-specific half of that),
|
|
24
|
+
* but retargeting an *arbitrary* effect (`fade-up target:h1`) has to be decided at compile/install
|
|
25
|
+
* time, before any primitive's own `prepare` ever runs. `core` must not depend on `effects` — every
|
|
26
|
+
* import in this codebase already runs the other way — so the shared half of the mechanism lives
|
|
27
|
+
* here and `effects/step-marking.ts` imports it back, rather than `core` reaching into `effects`.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Classify a selector as unusable, dangerously broad, or fine.
|
|
31
|
+
*
|
|
32
|
+
* `matches` against the two roots catches every selector that reaches the whole document — `*`,
|
|
33
|
+
* `html`, `body` and compounds like `*, a` with one rule and no bespoke parser — while leaving a
|
|
34
|
+
* deliberately scoped wildcard such as `.spy-nav > *` working, which a syntactic ban on `*` would
|
|
35
|
+
* not. `matches` throws on invalid syntax exactly as `querySelectorAll` does, so the same call
|
|
36
|
+
* still answers the validity question.
|
|
37
|
+
*
|
|
38
|
+
* @complexity O(1) time and space.
|
|
39
|
+
* @overallScore 100
|
|
40
|
+
*/
|
|
41
|
+
export declare function selectorBreadth(selector: string, doc: Document): 'invalid' | 'document-wide' | 'ok';
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a `target:` selector once at setup, rejecting both the unusable and the over-broad.
|
|
44
|
+
*
|
|
45
|
+
* Two distinct failures, one warning channel. An invalid selector thrown from inside the shared
|
|
46
|
+
* scheduler's frame callback would skip every other subscriber on that root, not just this one. An
|
|
47
|
+
* over-broad one is worse for being silent: `target:*` is perfectly valid syntax, so a syntax-only
|
|
48
|
+
* check passed it straight through to stamp an attribute onto every element in the document, on
|
|
49
|
+
* every frame.
|
|
50
|
+
*
|
|
51
|
+
* Rejecting rather than silently narrowing is the honest response, because there is no narrower
|
|
52
|
+
* selector that could be meant. A selector reaching `<html>` or `<body>` is not naming a set of
|
|
53
|
+
* steps or a nav link, it is naming the page, and guessing which of its thousands of descendants
|
|
54
|
+
* the author meant would be worse than saying so.
|
|
55
|
+
*
|
|
56
|
+
* @param selector - The authored `target:` value; empty is the no-op default, not an error.
|
|
57
|
+
* @param ctx - Prepare context, for `doc` and the warning channel.
|
|
58
|
+
* @param effect - Effect name, so the warning says which attribute to go and fix.
|
|
59
|
+
* @returns The selector, or `''` when it must be ignored.
|
|
60
|
+
* @complexity O(1) time and space; `matches` walks the selector, not the document.
|
|
61
|
+
* @overallScore 100
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveTarget(selector: string, ctx: TargetContext, effect: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Which tree a validated `target:` is searched in.
|
|
66
|
+
*
|
|
67
|
+
* `'self'` is the descendant reading — "search inside myself" — and is what `target:` means
|
|
68
|
+
* everywhere the word is used going forward. `'page'` is the whole document, which a handful of
|
|
69
|
+
* resolution sites need because the element they mark is deliberately somewhere else: `scroll-spy`'s
|
|
70
|
+
* nav link lives outside the section that activates it, and `media-scrub`'s frames may sit in a
|
|
71
|
+
* sibling figure.
|
|
72
|
+
*
|
|
73
|
+
* Two values, declared as a list rather than a boolean, so `'parent'` or `'section'` can be added
|
|
74
|
+
* later by extending {@link SCOPE_PARAM}'s `keywords` — a one-line change with no rename anywhere.
|
|
75
|
+
*/
|
|
76
|
+
export type TargetScope = 'self' | 'page';
|
|
77
|
+
/**
|
|
78
|
+
* The one declaration of `scope:`, shared by every primitive that declares `target:`.
|
|
79
|
+
*
|
|
80
|
+
* Declared once rather than restated per primitive for the same reason `resolveTarget` lives here
|
|
81
|
+
* rather than in each caller: `target:`/`scope:` is one convention across the library, and six
|
|
82
|
+
* copies of a `keywords` list is six places for it to drift.
|
|
83
|
+
*
|
|
84
|
+
* `default: ''` is load-bearing, not a style choice. `readParams` (`core/js-params.ts`) seeds
|
|
85
|
+
* `out[name] = spec.default` for *every* declared parameter before reading authored values, and
|
|
86
|
+
* `createParams`' `text` accessor uses `??`, so a non-empty schema default would make the
|
|
87
|
+
* caller-supplied fallback in {@link scopeParam} unreachable. `scroll-spy` is one primitive with
|
|
88
|
+
* one `ParameterSchema` whose two forms need opposite defaults (`'page'` for the per-section form,
|
|
89
|
+
* `'self'` for the container form), so "unset" has to be spellable — which is exactly why `target`
|
|
90
|
+
* itself is already declared `default: ''` on every primitive that owns the parameter.
|
|
91
|
+
*
|
|
92
|
+
* `type: 'keyword'`, not `'text'`: a `text` param accepts any string that survives the escape
|
|
93
|
+
* screen, so `scope:pgae` would be accepted in silence and then read as unset. `keyword` is the
|
|
94
|
+
* type whose `keywords` list is the *whole* grammar, so a typo is named. The cost is that
|
|
95
|
+
* `--kui-scope` reaches
|
|
96
|
+
* `element.style` when authored, where nothing reads it; an inert custom property is a much smaller
|
|
97
|
+
* price than a typo that fails silently.
|
|
98
|
+
*/
|
|
99
|
+
export declare const SCOPE_PARAM: ParamSpec;
|
|
100
|
+
/**
|
|
101
|
+
* Read `scope:`, falling back to the resolution site's own historical default.
|
|
102
|
+
*
|
|
103
|
+
* The fallback is per *call site*, not per primitive, because `scroll-spy` resolves the same
|
|
104
|
+
* declared parameter document-wide in one form and descendant-scoped in the other.
|
|
105
|
+
*
|
|
106
|
+
* @param params - The effect's validated parameters.
|
|
107
|
+
* @param fallback - What this site scoped to before `scope:` existed. Passing today's behaviour
|
|
108
|
+
* here is what makes declaring the parameter a no-op for existing markup.
|
|
109
|
+
* @returns The authored scope, or `fallback` when unset or unrecognised.
|
|
110
|
+
* @complexity O(1) time and space.
|
|
111
|
+
* @overallScore 100
|
|
112
|
+
*/
|
|
113
|
+
export declare function scopeParam(params: EffectParams, fallback: TargetScope): TargetScope;
|
|
114
|
+
/**
|
|
115
|
+
* Resolve a validated `target:` selector under a scope.
|
|
116
|
+
*
|
|
117
|
+
* {@link resolveTarget} has already run: this does no validation of its own, so an invalid or
|
|
118
|
+
* document-wide selector must have been rejected before it gets here.
|
|
119
|
+
*
|
|
120
|
+
* @param el - The authored host. The search root under `'self'`.
|
|
121
|
+
* @param ctx - Prepare context, for `doc` — the search root under `'page'`.
|
|
122
|
+
* @param selector - A validated, non-empty `target:` value.
|
|
123
|
+
* @param scope - Which tree to search.
|
|
124
|
+
* @returns The matches, in document order.
|
|
125
|
+
* @complexity O(n) time and space in matches; the query itself is the DOM's.
|
|
126
|
+
* @overallScore 100
|
|
127
|
+
*/
|
|
128
|
+
export declare function queryScoped(el: Element, ctx: Pick<TargetContext, 'doc'>, selector: string, scope: TargetScope): Element[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Reporter } from './reporter.js';
|
|
2
|
+
/** The subset of an observed binding's state this diagnostic reads and updates. */
|
|
3
|
+
export interface ThresholdReachabilityState {
|
|
4
|
+
/** Where to report an unreachable threshold. `undefined` means nowhere — nothing to check for. */
|
|
5
|
+
reporter?: Reporter;
|
|
6
|
+
/**
|
|
7
|
+
* Whether this element has ever been geometrically inside the root, independent of whether it
|
|
8
|
+
* ever met the authored threshold. Tells a genuine leave (toured through, never got there) apart
|
|
9
|
+
* from the first delivery of an element that starts outside the root (never arrived yet) — both
|
|
10
|
+
* report `isIntersecting: false`, and only one of them is evidence of anything.
|
|
11
|
+
*/
|
|
12
|
+
sawIntersecting: boolean;
|
|
13
|
+
/** Whether the threshold was ever actually met. A binding that got here has never met it. */
|
|
14
|
+
entered: boolean;
|
|
15
|
+
/** Set once this has warned, so a repeated tour past the same unreachable threshold stays quiet. */
|
|
16
|
+
thresholdWarned: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Say so when a full transit closes without an authored threshold ever being met, and the target's
|
|
20
|
+
* own geometry is why it never can be: it is larger than the root it scrolls through, so its
|
|
21
|
+
* intersection ratio has a hard ceiling below what was asked for. See {@link maxReachableRatio}.
|
|
22
|
+
*
|
|
23
|
+
* Checked only on a genuine leave — `!entry.isIntersecting` once {@link ThresholdReachabilityState.sawIntersecting}
|
|
24
|
+
* already recorded a real entry — rather than on whichever delivery arrives first. A target's
|
|
25
|
+
* measured size is a runtime fact, not an authored one, and the very first delivery for a freshly
|
|
26
|
+
* observed element can land before a lazy image has decoded, an accordion has expanded, or a web
|
|
27
|
+
* font has swapped in — any of which can measure a box taller *right now* than it will be once the
|
|
28
|
+
* page settles. A completed transit — inside the root and back out — gives layout the whole scroll
|
|
29
|
+
* past it to settle before its geometry is trusted for a diagnostic that, once given, cannot be
|
|
30
|
+
* unsaid. The cost is a symmetrical blind spot: a target that is too tall only *during* one transit
|
|
31
|
+
* (a mid-scroll resize) keeps quiet until the next full transit re-checks it — the same shape of
|
|
32
|
+
* tradeoff `meetsThreshold`'s own caveat already accepts for the threshold itself.
|
|
33
|
+
*
|
|
34
|
+
* An element that starts already outside the root also delivers `isIntersecting: false` on its very
|
|
35
|
+
* first entry — "hasn't arrived yet," not "toured through and failed" — which is exactly what
|
|
36
|
+
* `sawIntersecting` exists to tell apart from a real leave.
|
|
37
|
+
*
|
|
38
|
+
* Warns once per element (`thresholdWarned`), not once per delivery or per transit: an element that
|
|
39
|
+
* tours past its unreachable threshold repeatedly (a reader scrolling up and down) would otherwise
|
|
40
|
+
* repeat the same diagnostic every time.
|
|
41
|
+
*
|
|
42
|
+
* @complexity O(1) time, O(1) space.
|
|
43
|
+
* @overallScore 100
|
|
44
|
+
*/
|
|
45
|
+
export declare function checkThresholdReachability(binding: ThresholdReachabilityState, entry: IntersectionObserverEntry, ratio: number): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** The ancestor marker read by library animation creation and the showcase speed control. */
|
|
2
|
+
export declare const TIME_SCALE_ATTR = "data-kui-time-scale";
|
|
3
|
+
/**
|
|
4
|
+
* Read the nearest time scale, including a marker on the element itself.
|
|
5
|
+
*
|
|
6
|
+
* @complexity O(h) in ancestor depth; O(1) space.
|
|
7
|
+
* @overallScore 100
|
|
8
|
+
*/
|
|
9
|
+
export declare function timeScaleOf(el: Element): number;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { InstanceControl } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The four crossings, and what an author can ask for at each.
|
|
4
|
+
*
|
|
5
|
+
* A scroll trigger has four distinct moments, not two. Travelling down the page an element *enters*
|
|
6
|
+
* the viewport and later *leaves* it out of the top; travelling back up it re-enters from the top
|
|
7
|
+
* and leaves again out of the bottom. `data-kui-on="enter/leave"` covers two of them, with the only
|
|
8
|
+
* two verbs being "play" and "play backwards" — so the thing an author most often wants from a
|
|
9
|
+
* scroll trigger, *pause where you are and carry on from there when I come back*, could not be
|
|
10
|
+
* written at all.
|
|
11
|
+
*
|
|
12
|
+
* This module is the vocabulary: what the four crossings are called, what the eight verbs mean, and
|
|
13
|
+
* how one authored string becomes a verb per crossing. It touches no DOM and holds no state.
|
|
14
|
+
* `activation.ts` decides *which* crossing happened — it is the only thing holding the observer
|
|
15
|
+
* entry that can tell — and `animator.ts` owns the element and supplies the two element-level
|
|
16
|
+
* transitions the verbs are built from. Neither of them has to know the vocabulary, and this file
|
|
17
|
+
* does not have to know either of them.
|
|
18
|
+
*
|
|
19
|
+
* **Nothing here animates anything.** Every verb is composed from `InstanceControl` —
|
|
20
|
+
* `pause`/`resume`/`seek`, which `control.ts` has exposed as public API since runtime control
|
|
21
|
+
* shipped — plus `activate` and `reverse`, which only the animator can own because they are
|
|
22
|
+
* transitions of the element's whole state machine rather than of one playhead.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* One of the four moments a scroll crossing can happen at, named from the reader's direction of
|
|
26
|
+
* travel rather than the element's: `enter` and `leave` happen while scrolling *down*, the two
|
|
27
|
+
* `-back` crossings while scrolling back *up*.
|
|
28
|
+
*
|
|
29
|
+
* Hyphenated rather than camel-cased because these names are quoted back to authors in warnings,
|
|
30
|
+
* and every other word this library asks an author to read is hyphenated.
|
|
31
|
+
*/
|
|
32
|
+
export type Crossing = 'enter' | 'leave' | 'enter-back' | 'leave-back';
|
|
33
|
+
/** The four crossings in the order an author writes them — GSAP's `toggleActions` order. */
|
|
34
|
+
export declare const CROSSINGS: readonly Crossing[];
|
|
35
|
+
/**
|
|
36
|
+
* What to do at a crossing.
|
|
37
|
+
*
|
|
38
|
+
* The same eight GSAP's `toggleActions` takes, and the same words, because an author arriving from
|
|
39
|
+
* it should not have to learn a second vocabulary for an identical idea. What each one means here
|
|
40
|
+
* is stated in {@link applyToggleVerb}, which is also where the ones that cannot be honest about an
|
|
41
|
+
* element that has not started yet are handled.
|
|
42
|
+
*/
|
|
43
|
+
export type ToggleVerb = 'play' | 'pause' | 'resume' | 'reverse' | 'reset' | 'restart' | 'complete' | 'none';
|
|
44
|
+
/** A verb per crossing. Every slot is always present; an unwritten one is `none`. */
|
|
45
|
+
export type ToggleActions = Readonly<Record<Crossing, ToggleVerb>>;
|
|
46
|
+
/**
|
|
47
|
+
* Parse an authored `actions:` value into a verb per crossing.
|
|
48
|
+
*
|
|
49
|
+
* Total by construction: an unrecognised verb becomes `none` rather than failing the element, and
|
|
50
|
+
* every rejection is named. Trailing slots the author left off are `none` too, so `actions:play` is
|
|
51
|
+
* "play on the way in and nothing else" — which is both GSAP's own default and this library's
|
|
52
|
+
* existing behaviour for an unpaired `on:enter`.
|
|
53
|
+
*
|
|
54
|
+
* @param value - Authored text, e.g. `play/pause/resume/none`.
|
|
55
|
+
* @param warnings - Sink for the diagnostics. Optional so a pure parse can ignore them.
|
|
56
|
+
* @complexity O(1) time and space — at most four tokens.
|
|
57
|
+
* @overallScore 100
|
|
58
|
+
*/
|
|
59
|
+
export declare function parseToggleActions(value: string, warnings?: string[]): ToggleActions;
|
|
60
|
+
/**
|
|
61
|
+
* Diagnose an authored `actions:` value without a document — the shape-only half.
|
|
62
|
+
*
|
|
63
|
+
* Mirrors `validateActivation`'s contract exactly, including where it stops: this is the part
|
|
64
|
+
* `parse.ts` can run with nothing but the attribute text. Whether the *element* can honour the
|
|
65
|
+
* verbs needs its compiled plan, and lives in {@link warnAboutToggleActions}.
|
|
66
|
+
*
|
|
67
|
+
* @returns Zero warnings when every token is a verb.
|
|
68
|
+
* @complexity O(1) time and space.
|
|
69
|
+
* @overallScore 100
|
|
70
|
+
*/
|
|
71
|
+
export declare function validateToggleActions(value: string): string[];
|
|
72
|
+
/** What {@link warnAboutToggleActions} needs to know about the element, with no DOM in it. */
|
|
73
|
+
export interface ToggleDiagnostics {
|
|
74
|
+
actions: ToggleActions;
|
|
75
|
+
/** Whether this element's activation is delivered by the intersection observer at all. */
|
|
76
|
+
observed: boolean;
|
|
77
|
+
/** The authored activation, quoted back when it is not an observed one. */
|
|
78
|
+
activation: string;
|
|
79
|
+
/** Names of the composed effects rendered in JavaScript — they expose no playhead. */
|
|
80
|
+
jsEffectNames: readonly string[];
|
|
81
|
+
/** Whether the element's progress belongs to the scroller rather than to a clock. */
|
|
82
|
+
progressDriven: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Report every way an authored `actions:` cannot do what it says.
|
|
86
|
+
*
|
|
87
|
+
* All three of these produce a knob that exists and does nothing, which this codebase treats as
|
|
88
|
+
* worse than a missing knob — and all three are invisible from the markup, because the element
|
|
89
|
+
* still animates, just not the way the attribute claims.
|
|
90
|
+
*
|
|
91
|
+
* @returns One message per problem; empty when the element can honour every verb.
|
|
92
|
+
* @complexity O(1) time and space — four crossings.
|
|
93
|
+
* @overallScore 100
|
|
94
|
+
*/
|
|
95
|
+
export declare function warnAboutToggleActions(request: ToggleDiagnostics): string[];
|
|
96
|
+
/** The element a verb is being applied to, reduced to what the verbs actually need. */
|
|
97
|
+
export interface ToggleTarget {
|
|
98
|
+
/**
|
|
99
|
+
* Whether the element has been activated at all.
|
|
100
|
+
*
|
|
101
|
+
* The distinction two of the verbs cannot be honest without. An element that has never started is
|
|
102
|
+
* already sitting at its from-state, so `reset` there is a no-op rather than a seek, and `resume`
|
|
103
|
+
* means *start* rather than *unpause* — going straight to the ledger's play-state would open the
|
|
104
|
+
* compiled gate behind the animator's back, leaving `data-kui-state` reading `ready` on a visibly
|
|
105
|
+
* running element and no `kui:start` ever dispatched.
|
|
106
|
+
*/
|
|
107
|
+
started: boolean;
|
|
108
|
+
/** The element's reachable playheads. Empty for a JS-rendered or scroll-driven element. */
|
|
109
|
+
controls: readonly InstanceControl[];
|
|
110
|
+
/** Start, or turn a reversing playhead back around — `Animator.activate`. */
|
|
111
|
+
activate(): void;
|
|
112
|
+
/** Play out backwards to the from-state — `Animator.reverseFrom`. */
|
|
113
|
+
reverse(): void;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Do one verb.
|
|
117
|
+
*
|
|
118
|
+
* Every branch is composed from things that already existed. `play` and `reverse` are the element's
|
|
119
|
+
* two directional transitions, which only the animator owns; the other five are `InstanceControl`
|
|
120
|
+
* calls. Nothing here touches an `Animation` object, and nothing here decides *when* — that is the
|
|
121
|
+
* observer's job.
|
|
122
|
+
*
|
|
123
|
+
* The three jump verbs are worth stating precisely, because "a jump" and "a state machine" are easy
|
|
124
|
+
* to get wrong together:
|
|
125
|
+
*
|
|
126
|
+
* - **`reset`** rewinds to the from-state and stops there. On an element that never started there
|
|
127
|
+
* is nothing to rewind — it is already at the from-state — so this does nothing at all rather
|
|
128
|
+
* than seeking playheads still sitting behind the compiled gate.
|
|
129
|
+
* - **`restart`** rewinds and keeps going. It activates first, so an element crossing for the first
|
|
130
|
+
* time starts properly — with its `kui:start`, its state attribute and its settle — rather than
|
|
131
|
+
* being seeked while still gated. On an already-running element `activate` returns at its own
|
|
132
|
+
* guard, so no second settle is armed and the seek simply replays the run.
|
|
133
|
+
* - **`complete`** jumps to the end state, and activates first for the same reason. It deliberately
|
|
134
|
+
* does *not* pause afterwards: an animation seeked to its end while still playing reaches
|
|
135
|
+
* `finished` on its own, and that is what resolves the settle and dispatches `kui:finish`.
|
|
136
|
+
* Pausing it there would strand the element at `running` forever with nothing moving.
|
|
137
|
+
*
|
|
138
|
+
* @complexity O(n) time in the element's controllable instances; O(1) space.
|
|
139
|
+
* @overallScore 100
|
|
140
|
+
*/
|
|
141
|
+
export declare function applyToggleVerb(verb: ToggleVerb, target: ToggleTarget): void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which way the reader is travelling, and which side of a scroll root that puts things on.
|
|
3
|
+
*
|
|
4
|
+
* Split out of `activation.ts` because it answers a question about the *reader* rather than about
|
|
5
|
+
* any element or any authored spec: one page-wide signal, sampled from one listener, that the
|
|
6
|
+
* four-way crossing delivery consults and nothing else in the binder cares about.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Which side of the observer's root an element sits on, in the reader's direction of travel:
|
|
10
|
+
* `before` is the side already scrolled past, `after` the side not reached yet.
|
|
11
|
+
*
|
|
12
|
+
* Named by travel rather than by geometry so one word covers both axes — `before` is above a
|
|
13
|
+
* vertically scrolled root and to the left of a horizontally scrolled one.
|
|
14
|
+
*/
|
|
15
|
+
export type RootSide = 'before' | 'after';
|
|
16
|
+
/** Which way the reader is travelling, phrased as the side an element entering *now* came from. */
|
|
17
|
+
export interface TravelTracker {
|
|
18
|
+
/**
|
|
19
|
+
* The side an element entering at `time` must have arrived from, or `undefined` when the reader's
|
|
20
|
+
* travel does not explain a delivery at that moment.
|
|
21
|
+
*/
|
|
22
|
+
arrivedFrom(time: number): RootSide | undefined;
|
|
23
|
+
/** Start listening, if this is the first four-way binding to ask. */
|
|
24
|
+
retain(): void;
|
|
25
|
+
/** Stop listening, if this was the last. */
|
|
26
|
+
release(): void;
|
|
27
|
+
/** Stop listening regardless of the count, for a binder torn down with bindings still on it. */
|
|
28
|
+
reset(): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Track which way the reader is travelling, so an entering delivery can be classified by what is
|
|
32
|
+
* happening *now* rather than by memory of a crossing that may never have been delivered.
|
|
33
|
+
*
|
|
34
|
+
* `deliverCrossing` used to answer "first entry or re-entry?" purely from `binding.outside`, the
|
|
35
|
+
* side recorded by the last *leaving* delivery. A scroll that lands in a single frame — an
|
|
36
|
+
* `<a href="#anchor">` click, `scrollIntoView()`, `scrollTo({ behavior: 'instant' })`, scroll
|
|
37
|
+
* restoration — carrying the element from "not intersecting" straight past to "not intersecting" on
|
|
38
|
+
* the far side changes neither `isIntersecting` nor the threshold ratio at any frame the browser
|
|
39
|
+
* samples, so the observer delivers *nothing at all* and the remembered side is left describing a
|
|
40
|
+
* position the reader is no longer anywhere near. The next real delivery is then classified against
|
|
41
|
+
* it and comes out backwards: measured in Chrome, a reader scrolling back up through a skipped
|
|
42
|
+
* element got `enter` where `enter-back` was authored. A denser threshold array does not help,
|
|
43
|
+
* because the browser paints no intermediate frame for it to sample; nor does re-reading the
|
|
44
|
+
* geometry, because an entering element is inside the root and so is on no side to read.
|
|
45
|
+
*
|
|
46
|
+
* The reader's direction of travel answers it without needing to know anything about the skipped
|
|
47
|
+
* crossing, and the two agree wherever nothing was skipped, so the ordinary path is unchanged.
|
|
48
|
+
*
|
|
49
|
+
* One listener for every binding on the page, mirroring `activation.ts`'s one-observer-per-threshold:
|
|
50
|
+
* direction is a property of the reader, not of an element, so per-element listeners would buy
|
|
51
|
+
* nothing for their cost. It is retained only by four-way bindings — a page of plain `on:enter`
|
|
52
|
+
* effects adds no listener at all.
|
|
53
|
+
*
|
|
54
|
+
* @returns A tracker whose `retain`/`release` ref-count the single shared listener.
|
|
55
|
+
* @complexity O(1) time per scroll event, independent of how many elements are bound; O(s) space in
|
|
56
|
+
* the number of distinct scrollers the reader has actually touched.
|
|
57
|
+
* @overallScore 100
|
|
58
|
+
*/
|
|
59
|
+
export declare function createTravelTracker(): TravelTracker;
|