kuinetic 0.2.1 → 0.2.3
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 +15 -8
- package/dist/esm/advanced/index.mjs +5 -5
- package/dist/esm/{chunk-U43BX5O2.mjs → chunk-FASAEQDR.mjs} +54 -7
- package/dist/esm/{chunk-BN2Y4UKB.mjs → chunk-FYMZW5N2.mjs} +2373 -541
- package/dist/esm/{chunk-RVTYN6NZ.mjs → chunk-MZQG2VMG.mjs} +208 -2
- package/dist/esm/core/index.mjs +21 -3
- package/dist/esm/effects/index.mjs +2 -2
- package/dist/esm/index.mjs +35 -4
- package/dist/esm/notes/index.mjs +1075 -0
- package/dist/kuinetic.advanced.js +63 -25
- package/dist/kuinetic.advanced.min.js +3 -3
- package/dist/kuinetic.all.js +2658 -538
- package/dist/kuinetic.all.min.js +2 -2
- package/dist/kuinetic.css +238 -65
- package/dist/kuinetic.js +2657 -537
- package/dist/kuinetic.min.css +1 -1
- package/dist/kuinetic.min.js +2 -2
- package/dist/kuinetic.notes.js +1 -0
- package/dist/types/3d/flattening.d.ts +8 -7
- package/dist/types/advanced/camera-3d.d.ts +0 -4
- package/dist/types/advanced/fluid-cursor.d.ts +0 -4
- package/dist/types/advanced/particles.d.ts +0 -4
- package/dist/types/advanced/scenes.d.ts +0 -4
- package/dist/types/browser/boot.d.ts +9 -4
- package/dist/types/core/activation.d.ts +11 -0
- package/dist/types/core/compile.d.ts +7 -0
- package/dist/types/core/deck-viewer.d.ts +87 -0
- package/dist/types/core/derived/types.d.ts +0 -1
- package/dist/types/core/describe.d.ts +174 -0
- package/dist/types/core/gesture.d.ts +16 -1
- package/dist/types/core/index.d.ts +6 -2
- package/dist/types/core/stagger.d.ts +1 -1
- package/dist/types/core/target.d.ts +28 -0
- package/dist/types/core/timing-contract.d.ts +61 -0
- package/dist/types/core/types.d.ts +59 -0
- package/dist/types/effects/auto-motion.d.ts +169 -0
- package/dist/types/effects/carousel/deck.d.ts +170 -0
- package/dist/types/effects/carousel/drag.d.ts +6 -0
- package/dist/types/effects/carousel/fit.d.ts +130 -0
- package/dist/types/effects/carousel/index.d.ts +6 -23
- package/dist/types/effects/carousel/stack.d.ts +77 -0
- package/dist/types/effects/catalog/interaction-reveal.d.ts +69 -9
- package/dist/types/effects/forms/primitives.d.ts +1 -2
- package/dist/types/effects/gestures/index.d.ts +3 -4
- package/dist/types/effects/shared.d.ts +3 -30
- package/dist/types/effects/step-index.d.ts +48 -10
- package/dist/types/effects/swipe-event.d.ts +75 -0
- package/dist/types/index.d.ts +26 -0
- package/dist/types/notes/catalog-core.d.ts +3 -0
- package/dist/types/notes/catalog.d.ts +3 -0
- package/dist/types/notes/index.d.ts +13 -0
- package/dist/types/notes/interaction-reveal.d.ts +3 -0
- package/dist/types/notes/interaction.d.ts +6 -0
- package/dist/types/notes/keys.d.ts +7 -0
- package/dist/types/notes/mechanics.d.ts +7 -0
- package/dist/types/notes/scroll-nav-forms.d.ts +6 -0
- package/dist/types/notes/shared.d.ts +6 -0
- package/dist/types/notes/showcase.d.ts +7 -0
- package/dist/types/notes/text.d.ts +3 -0
- package/dist/types/showcase/compare.d.ts +9 -0
- package/dist/types/showcase/index.d.ts +3 -0
- package/dist/types/showcase/lightbox.d.ts +23 -1
- package/dist/types/showcase/modal-shell.d.ts +11 -0
- package/package.json +10 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
2
|
import type { Registry } from '../../core/registry.js';
|
|
3
|
+
export { snapPosition } from './deck.js';
|
|
3
4
|
/**
|
|
4
5
|
* Whether a slot at this ring angle has its own plane turned away from the camera.
|
|
5
6
|
*
|
|
@@ -24,25 +25,6 @@ export declare function faceAt(angleDeg: number): 'front' | 'back';
|
|
|
24
25
|
* @complexity O(1) time and space.
|
|
25
26
|
*/
|
|
26
27
|
export declare function normaliseDegrees(angleDeg: number): number;
|
|
27
|
-
/**
|
|
28
|
-
* Where the ring's continuous position rounds to, and how far past it the ring currently sits.
|
|
29
|
-
*
|
|
30
|
-
* The pair the stylesheet needs and the reason both tokens are published rather than one: the
|
|
31
|
-
* integer decides which slot is *live* (and therefore what `--kui-offset` means for every other
|
|
32
|
-
* slot), and the remainder is the sub-step rotation on top of it. Re-snapping the integer whenever
|
|
33
|
-
* the remainder passes a half step is what keeps the remainder inside ±0.5, so the ring never has
|
|
34
|
-
* to travel back across the whole strip to wrap from the last slide to the first — the same
|
|
35
|
-
* property `circularOffset` gives the discrete case.
|
|
36
|
-
*
|
|
37
|
-
* @param position - Continuous ring position, in places. May be negative or beyond the count.
|
|
38
|
-
* @param total - How many places the ring has.
|
|
39
|
-
* @returns The live integer step, wrapped into range, and the signed remainder in `[-0.5, 0.5)`.
|
|
40
|
-
* @complexity O(1) time and space.
|
|
41
|
-
*/
|
|
42
|
-
export declare function snapPosition(position: number, total: number): {
|
|
43
|
-
step: number;
|
|
44
|
-
drift: number;
|
|
45
|
-
};
|
|
46
28
|
/**
|
|
47
29
|
* An authored `<angle>` as a number of degrees.
|
|
48
30
|
*
|
|
@@ -65,8 +47,9 @@ export declare function degreesOf(value: string, fallbackDeg: number): number;
|
|
|
65
47
|
/**
|
|
66
48
|
* The ring primitive.
|
|
67
49
|
*
|
|
68
|
-
* `renderer: 'javascript'` and
|
|
69
|
-
* nothing itself — it publishes numbers
|
|
50
|
+
* `renderer: 'javascript'` and its channels together are the honest declaration: this file paints
|
|
51
|
+
* nothing itself — it publishes numbers (and the `data-kui-step` index, its third channel) — but
|
|
52
|
+
* `carousel.css` does, and both of the things that stylesheet writes
|
|
70
53
|
* have to be declared or the conflict detector waves through a composition that silently loses one
|
|
71
54
|
* effect's output.
|
|
72
55
|
*
|
|
@@ -116,7 +99,7 @@ export declare const CAROUSEL_PRIMITIVES: Primitive[];
|
|
|
116
99
|
*
|
|
117
100
|
* `phase: 'idle'` on all of them too, and for the same reason `target` does not change the answer:
|
|
118
101
|
* `channelsFor`/`findConflicts` (`core/channels.ts`) reason about the channels *this preset
|
|
119
|
-
* declares* on the host it is authored on (`CHANNEL.skew` and `
|
|
102
|
+
* declares* on the host it is authored on (`CHANNEL.skew`, `'discrete'` and `data-kui-step`, from
|
|
120
103
|
* `SPATIAL_RING_PRIMITIVE`), not about which element in the subtree physically paints them — the
|
|
121
104
|
* compiler has no notion of "the primitive's own target moved the real work three nodes down," and
|
|
122
105
|
* does not need one here, because `requiresOwnSubtree` already keeps this preset off of anything
|
|
@@ -135,7 +118,7 @@ export declare const CAROUSEL_PRIMITIVES: Primitive[];
|
|
|
135
118
|
* over the `transform` shorthand (`CHANNEL.skew`) for the ring's whole lifetime, and there is no
|
|
136
119
|
* turn-taking to model. It replaces "nothing is known" with the actual, checked fact.
|
|
137
120
|
*
|
|
138
|
-
* None of
|
|
121
|
+
* None of them declares `transitions`: nothing here renders through a CSS `transition:` on the
|
|
139
122
|
* host box for `phaseOf` to derive `state` from — the ring is `renderer: 'javascript'` throughout.
|
|
140
123
|
*/
|
|
141
124
|
export declare const CAROUSEL_PRESETS: Preset[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
|
+
import type { Face } from './deck.js';
|
|
3
|
+
/**
|
|
4
|
+
* A deck of cards receding diagonally into depth — the front card large and sharp, each one behind
|
|
5
|
+
* it stepped up and to the side, smaller, softer and fainter — that cycles forward: the front card
|
|
6
|
+
* leaves toward the viewer and the next one comes up to take its place.
|
|
7
|
+
*
|
|
8
|
+
* ## Why a primitive of its own, sharing the ring's deck
|
|
9
|
+
*
|
|
10
|
+
* The *index* is the ring's exactly: N slots, a live one, a signed place for every other, a real-
|
|
11
|
+
* valued position a drag moves between two places, `next:`/`prev:`/`jump:`, the keyboard, `spin:`
|
|
12
|
+
* and `autoplay:`. All of that is `deck.ts`, shared, so the two can never disagree about where a
|
|
13
|
+
* deck is. What differs is not a parameter of the ring's shape but a different shape:
|
|
14
|
+
*
|
|
15
|
+
* - **It is linear, not circular.** A ring spaces its slots by `arc / count` and has no front or
|
|
16
|
+
* back in the index; a stack has a front, and the slots behind it are *further*, not *around*.
|
|
17
|
+
* Not one of the ring's seven geometry parameters (`tilt`, `arc`, `radius`, `gap`, `perspective`,
|
|
18
|
+
* `facing`, `plane`) means anything for it, and none of its six means anything for a ring.
|
|
19
|
+
* - **It paints more channels.** Receding cards blur and fade, so a stack writes `filter` and
|
|
20
|
+
* `opacity` on its slots as well as `transform`. Declaring those on the ring primitive would have
|
|
21
|
+
* made every existing `carousel-3d` start colliding with any opacity or filter effect composed
|
|
22
|
+
* beside it — a behaviour change to four shipped names in order to add a fifth.
|
|
23
|
+
*
|
|
24
|
+
* So: one deck, two primitives, each declaring only its own geometry and its own channels.
|
|
25
|
+
*
|
|
26
|
+
* ## Which cards are showing
|
|
27
|
+
*
|
|
28
|
+
* The marker publishes circular offsets (`-N/2 … N/2`), which suits a ring. A stack reads them
|
|
29
|
+
* shifted — `mod(offset + 1, N) - 1`, in `carousel.css` — onto `-1 … N-2`: the live card at 0,
|
|
30
|
+
* `depth` cards visible behind it, and exactly one place *in front*, for the card that has just
|
|
31
|
+
* left. The wrap from the front to the back therefore happens between `-1` (faded out in front)
|
|
32
|
+
* and `N-2` (faded out behind), so a card being recycled never crosses the visible stack. With
|
|
33
|
+
* fewer than `depth + 3` cards the back slot is still visible when it arrives, and the recycled card
|
|
34
|
+
* fades in there as it lands, which is the honest limit of a short deck.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Whether a slot at this stack place is showing: in front of the fade-out behind the last visible
|
|
38
|
+
* card, and not yet gone out past the viewer.
|
|
39
|
+
*
|
|
40
|
+
* Pure and exported so the boundaries are assertable without a DOM. Mirrors the opacity ramp in
|
|
41
|
+
* `carousel.css`: a card that has left is gone by half a place in front, and the back of the stack
|
|
42
|
+
* fades out over the one place past `depth`.
|
|
43
|
+
*
|
|
44
|
+
* @param offset - The slot's circular offset, as the marker published it.
|
|
45
|
+
* @param drift - The deck's sub-step position past the live place.
|
|
46
|
+
* @param count - The deck's size.
|
|
47
|
+
* @param depth - How many cards show behind the front one.
|
|
48
|
+
* @returns `'front'` while any of the card shows, `'back'` once none of it does.
|
|
49
|
+
* @complexity O(1) time and space.
|
|
50
|
+
*/
|
|
51
|
+
export declare function stackFace(offset: number, drift: number, count: number, depth: number): Face;
|
|
52
|
+
/**
|
|
53
|
+
* A circular offset re-read as a place in the stack: `0` in front, `1…N-2` behind, `-1` just left.
|
|
54
|
+
*
|
|
55
|
+
* The JavaScript twin of `carousel.css`'s `mod(offset + 1, N) - 1`; the two must agree, and this is
|
|
56
|
+
* the one the tests pin.
|
|
57
|
+
*
|
|
58
|
+
* @complexity O(1) time and space.
|
|
59
|
+
*/
|
|
60
|
+
export declare function stackPlace(offset: number, count: number): number;
|
|
61
|
+
/**
|
|
62
|
+
* The stack primitive.
|
|
63
|
+
*
|
|
64
|
+
* `skew` for the `transform` shorthand (the scale and the diagonal step compose in one function
|
|
65
|
+
* list), `opacity` and `filter` for the recession, `discrete` for the host's `display: grid` — the
|
|
66
|
+
* same reasoning `SPATIAL_RING_PRIMITIVE` gives for its own pair, widened by what a stack paints.
|
|
67
|
+
* `reducedMotion: 'shorten'` for the same reason as the ring: the deck must keep working, and the
|
|
68
|
+
* policy layer already shortens the transition, while `deck.ts` starts any auto-motion paused.
|
|
69
|
+
*/
|
|
70
|
+
export declare const SPATIAL_STACK_PRIMITIVE: Primitive;
|
|
71
|
+
/**
|
|
72
|
+
* `carousel-stack` — the family name first, the shape second, as `carousel-fade`/`carousel-slide`/
|
|
73
|
+
* `carousel-3d` already read. One name: the diagonal's direction is two signed lengths (`shift:`,
|
|
74
|
+
* `rise:`), so a left-hand or downward stack is a parameter, not a catalogue entry. `requiresOwnSubtree`
|
|
75
|
+
* and `phase: 'idle'` for the reasons `CAROUSEL_PRESETS` gives for the ring.
|
|
76
|
+
*/
|
|
77
|
+
export declare const STACK_PRESETS: Preset[];
|
|
@@ -1,4 +1,57 @@
|
|
|
1
1
|
import type { Preset, Primitive } from '../../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* ### `PLACEMENT` — `place:top|bottom|auto`, and the one piece of JavaScript it costs
|
|
4
|
+
*
|
|
5
|
+
* `label-swap`'s and `anchored-preview`'s doc comments both record why a placement *keyword* was
|
|
6
|
+
* once refused: CSS cannot branch on a custom property's value without `@container style()`, so
|
|
7
|
+
* the word has to be read by script. That is still true, and this is the script — kept to the one
|
|
8
|
+
* job CSS genuinely cannot do, so the geometry itself stays in the stylesheet.
|
|
9
|
+
*
|
|
10
|
+
* The script never writes a length. It stamps the *resolved side* as an attribute on the host
|
|
11
|
+
* (`data-kui-hint-place` / `data-kui-preview-place`), and `interaction.css` keys the geometry on
|
|
12
|
+
* `[data-kui-fx][data-kui-…-place='…']`: a compound on the host itself, no combinator, so the
|
|
13
|
+
* `INHERITED_STATE` contract above holds. The `[data-kui-fx]`
|
|
14
|
+
* half lifts the rule to two attributes of specificity, which is what lets an explicit side beat
|
|
15
|
+
* an `anchored-preview-left` preset rule regardless of source order.
|
|
16
|
+
*
|
|
17
|
+
* A fixed side is stamped once at activation. Measuring happens only when the part is about to be
|
|
18
|
+
* seen: on `pointerenter`/`focusin`, the two events that precede every state the stylesheet reveals
|
|
19
|
+
* on (`:hover`, `:focus-visible`, and the coarse-pointer `:active`, which a touch `pointerenter`
|
|
20
|
+
* also precedes). While shown, a passive capture-phase `scroll` and a `resize` listener re-measure
|
|
21
|
+
* at most once per frame, because the reader can scroll a hovered trigger into the viewport edge;
|
|
22
|
+
* both are removed the moment neither hover nor focus remains. Nothing runs per frame while the
|
|
23
|
+
* part is hidden. `auto` measures which side; every side, fixed or not, measures `SHIFT` below.
|
|
24
|
+
*
|
|
25
|
+
* The flip is along the preferred side's own axis only — top↔bottom, left↔right. Moving a
|
|
26
|
+
* left-anchored preview to the top is a different layout, not a correction of this one.
|
|
27
|
+
*/
|
|
28
|
+
type Side = 'top' | 'bottom' | 'left' | 'right';
|
|
29
|
+
/**
|
|
30
|
+
* Pick the side a part should sit on, given the room around its trigger.
|
|
31
|
+
*
|
|
32
|
+
* The preferred side wins whenever the part fits there. When it does not, the opposite side is
|
|
33
|
+
* taken only if it has *more* room — not merely if it fits — so a part too big for either side
|
|
34
|
+
* lands where the least of it is clipped rather than flipping to a side that clips more.
|
|
35
|
+
*
|
|
36
|
+
* @param preferred - The side to try first.
|
|
37
|
+
* @param room - Free space, in CSS px, between each edge of the trigger and the viewport.
|
|
38
|
+
* @param extent - How far the part reaches from the trigger along its axis, gap included.
|
|
39
|
+
* @complexity O(1) time and space.
|
|
40
|
+
*/
|
|
41
|
+
export declare function chooseSide(preferred: Side, room: Record<Side, number>, extent: number): Side;
|
|
42
|
+
/**
|
|
43
|
+
* How far to move a span on one axis so it sits inside `[margin, viewport - margin]`.
|
|
44
|
+
*
|
|
45
|
+
* A span longer than that room cannot fit either way, so it is aligned to the start edge — the
|
|
46
|
+
* reading edge, where its first words are.
|
|
47
|
+
*
|
|
48
|
+
* @param start - The span's start coordinate in the viewport, unshifted, in CSS px.
|
|
49
|
+
* @param size - Its length on the same axis.
|
|
50
|
+
* @param viewport - The viewport's length on that axis.
|
|
51
|
+
* @returns The signed offset to add, `0` when it already fits.
|
|
52
|
+
* @complexity O(1) time and space.
|
|
53
|
+
*/
|
|
54
|
+
export declare function shiftWithin(start: number, size: number, viewport: number, margin?: number): number;
|
|
2
55
|
/**
|
|
3
56
|
* The masked label swap: two stacked copies of a label in a clipped box, one sliding out as the
|
|
4
57
|
* other slides in.
|
|
@@ -148,15 +201,15 @@ export declare const HOVER_INTENT_PRESETS: Preset[];
|
|
|
148
201
|
* That mechanism needs no feature detection and no fallback branch, because it already works in
|
|
149
202
|
* every browser the rest of the catalog does.
|
|
150
203
|
*
|
|
151
|
-
* ###
|
|
204
|
+
* ### Four preset names, and a `place:` parameter behind them
|
|
152
205
|
*
|
|
153
|
-
* The
|
|
154
|
-
* custom property's *value
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
206
|
+
* The four names came first, for the reasoning `label-swap`'s own doc comment gives for its axis:
|
|
207
|
+
* CSS cannot branch on a custom property's *value*, so a placement keyword has to be read by
|
|
208
|
+
* JavaScript. `place:auto` — flip to whichever side fits the viewport — is a measurement CSS cannot
|
|
209
|
+
* make at all, so that JavaScript now exists (`PLACEMENT`, above), and the names became four
|
|
210
|
+
* spellings of `place:` (each preset carries its side in `params`). They stay because they are how
|
|
211
|
+
* the rest of the catalog spells "one mechanism, several fixed shapes" (`masked-label-swap-x`,
|
|
212
|
+
* `card-flip-y`), and because the stylesheet still places them before any script has run.
|
|
160
213
|
*
|
|
161
214
|
* `anchored-preview` (no suffix) is `top`, matching `hover-intent`'s own default placement for the
|
|
162
215
|
* same reason: a name tag or a tooltip reads naturally above its trigger unless told otherwise.
|
|
@@ -171,7 +224,13 @@ export declare const HOVER_INTENT_PRESETS: Preset[];
|
|
|
171
224
|
* absolute; inset: var(--kui-anchored-preview-inset); translate: var(--kui-anchored-preview-travel)`
|
|
172
225
|
* and nothing placement-specific. No rule anywhere carries a combinator after an fx compound, so
|
|
173
226
|
* `anchored-preview` never enters the reaching-selector set `css-requires-own-subtree.test.ts`
|
|
174
|
-
* derives
|
|
227
|
+
* derives and needs no `requiresOwnSubtree`.
|
|
228
|
+
*
|
|
229
|
+
* ### `target:` names the preview
|
|
230
|
+
*
|
|
231
|
+
* `data-kui="anchored-preview-bottom target:.preview-img"` needs no `data-kui-preview` in the
|
|
232
|
+
* markup: the effect stays on the trigger and the library stamps the marker on the match, then
|
|
233
|
+
* takes it off again on teardown — see `claimPart`.
|
|
175
234
|
*
|
|
176
235
|
* ### Channels
|
|
177
236
|
*
|
|
@@ -246,3 +305,4 @@ export declare const SEARCH_EXPAND_PRESETS: Preset[];
|
|
|
246
305
|
/** All four reveal-shaped families, for `interaction.ts`'s catalog-wide exports. */
|
|
247
306
|
export declare const REVEAL_PRIMITIVES: Primitive[];
|
|
248
307
|
export declare const REVEAL_PRESETS: Preset[];
|
|
308
|
+
export {};
|
|
@@ -15,8 +15,7 @@ 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
|
|
19
|
-
export type { ControlGroup } from '../step-index.js';
|
|
18
|
+
export { nextStep, prevStep, clampStep, countSteps } from '../step-index.js';
|
|
20
19
|
/**
|
|
21
20
|
* The only member of the family that is not `reducedMotion: 'disable'`, and the difference is the
|
|
22
21
|
* whole widget.
|
|
@@ -41,10 +41,9 @@ import type { Registry } from '../../core/registry.js';
|
|
|
41
41
|
* there via the same inline style until the next drag picks it up again. None of that is an
|
|
42
42
|
* `entrance`: there is no hidden from-state and no `cloak`, just an idle rest value between
|
|
43
43
|
* gestures.
|
|
44
|
-
* - `swipeable` (`swipe`, `swipe-x`) and `pressable` (`long-press`)
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* `EffectPhase` value only in spelling, not in meaning). `long-press` is the clean case: it
|
|
44
|
+
* - `swipeable` (`swipe`, `swipe-x`, `swipe-y`) and `pressable` (`long-press`) write an attribute,
|
|
45
|
+
* not a CSS property, and their *channel* names it (`attributeChannel` in `core/types.ts`) —
|
|
46
|
+
* which is unrelated to this `state` *phase*. `long-press` is the clean case: it
|
|
48
47
|
* flips `data-kui-pressed` on the hold and back off on release, precisely the `:active`-shaped
|
|
49
48
|
* "held only while the visitor is doing something" case the `state` phase doc describes.
|
|
50
49
|
* `swipe`/`swipe-x` are a discrete, already-over gesture by the time `data-kui-swipe` is
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PrepareContext } from '../core/effect-context.js';
|
|
2
|
+
import type { TimingContract, TimingToken } from '../core/timing-contract.js';
|
|
2
3
|
import type { Activation, EffectParams, NamedActivation, ParameterSchema, Primitive, ReducedMotionPolicy, Timeline } from '../core/types.js';
|
|
3
4
|
/**
|
|
4
5
|
* The one `delay` declaration, spread by every primitive that waits for a trigger before playing.
|
|
@@ -24,36 +25,8 @@ import type { Activation, EffectParams, NamedActivation, ParameterSchema, Primit
|
|
|
24
25
|
* pick this up.
|
|
25
26
|
*/
|
|
26
27
|
export declare const TRIGGER_DELAY_PARAM: ParameterSchema;
|
|
27
|
-
|
|
28
|
-
export type
|
|
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
|
-
}
|
|
28
|
+
export { ALL_TIMING_TOKENS } from '../core/timing-contract.js';
|
|
29
|
+
export type { TimingContract, TimingToken } from '../core/timing-contract.js';
|
|
57
30
|
/**
|
|
58
31
|
* How long an effect waits after its trigger before it starts, in milliseconds.
|
|
59
32
|
*
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import type { Cleanup, EffectParams } from '../core/types.js';
|
|
2
2
|
import type { PrepareContext } from '../core/effect-context.js';
|
|
3
|
-
import type { TargetScope } from '../core/target.js';
|
|
4
3
|
export interface StepIndexOptions {
|
|
5
4
|
el: Element;
|
|
6
5
|
params: EffectParams;
|
|
7
6
|
ctx: PrepareContext;
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The steps, already scoped by the caller's `scope:`. Controls take no scope — see
|
|
9
|
+
* `core/target.ts`'s `queryControls`.
|
|
10
|
+
*/
|
|
9
11
|
resolveSteps: () => Iterable<Element>;
|
|
10
12
|
onRender?: (index: number, total: number) => void;
|
|
11
13
|
clickFallback?: boolean;
|
|
12
14
|
name?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Further control groups bound through the same delegated listener as `next:`/`prev:`/`jump:` —
|
|
17
|
+
* a deck's `pause:` (`auto-motion.ts`). Like the three, naming one retires the click-the-container
|
|
18
|
+
* fallback: a pause button inside the deck would otherwise also advance it on every press.
|
|
19
|
+
*/
|
|
20
|
+
controls?: readonly ControlGroup[];
|
|
21
|
+
/**
|
|
22
|
+
* Called before a *person* moves the index — a named control or the container click — and never
|
|
23
|
+
* for `goTo`/`next`/`prev` called from code. How a deck's autoplay yields to the visitor without
|
|
24
|
+
* mistaking its own timer for one.
|
|
25
|
+
*/
|
|
26
|
+
onInput?: () => void;
|
|
13
27
|
}
|
|
14
28
|
export interface StepIndex {
|
|
15
29
|
goTo(index: number): void;
|
|
@@ -80,7 +94,28 @@ export interface ControlGroup {
|
|
|
80
94
|
run: (node: Element, position: number) => void;
|
|
81
95
|
}
|
|
82
96
|
/**
|
|
83
|
-
*
|
|
97
|
+
* Read one control parameter into a group, or `null` when it is not authored or not usable.
|
|
98
|
+
*
|
|
99
|
+
* The one place a control selector is resolved, for every deck: `next:`/`prev:`/`jump:` on the
|
|
100
|
+
* step deck and the spatial decks, and `pause:` (`auto-motion.ts`). Where it is then *searched* is
|
|
101
|
+
* {@link queryControls}' rule — inside the host first, the page only when nothing inside matches —
|
|
102
|
+
* and deliberately not `scope:`, which is the steps' alone (see that function for why).
|
|
103
|
+
*
|
|
104
|
+
* Registered even when nothing matches yet, with a warning: a control the page renders after setup
|
|
105
|
+
* is still reached, because {@link delegateControls} re-queries on every press.
|
|
106
|
+
*
|
|
107
|
+
* @param label - The effect name an author would recognise, for the warning.
|
|
108
|
+
* @complexity O(n) in one setup query's matches; O(1) space beyond the group.
|
|
109
|
+
* @overallScore 100
|
|
110
|
+
*/
|
|
111
|
+
export declare function bindControl(request: {
|
|
112
|
+
el: Element;
|
|
113
|
+
ctx: PrepareContext;
|
|
114
|
+
params: EffectParams;
|
|
115
|
+
label: string;
|
|
116
|
+
}, param: string, run: ControlGroup['run']): ControlGroup | null;
|
|
117
|
+
/**
|
|
118
|
+
* Bind every named control group with a *single* delegated listener.
|
|
84
119
|
*
|
|
85
120
|
* One listener per matched node was the obvious form and it had a hole with no warning attached:
|
|
86
121
|
* the nodes are matched once, at prepare time, so a dot rendered afterwards was inert forever. The
|
|
@@ -90,23 +125,26 @@ export interface ControlGroup {
|
|
|
90
125
|
* the selector at the moment of the press, which is the same rule `resolveSteps` and `countSteps`
|
|
91
126
|
* already follow, and it collapses teardown from N removals to one.
|
|
92
127
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
128
|
+
* Two listeners, each owning half the presses. A control may sit outside the element it drives —
|
|
129
|
+
* arrows in a section header, one pause button for a band of rings — which {@link queryControls}
|
|
130
|
+
* reaches by falling back to the page, so the document listens for presses *outside* the host. The
|
|
131
|
+
* host listens for presses inside itself, which keeps a detached or shadow-hosted deck working and
|
|
132
|
+
* bounds the walk below at the host: with no control inside, a wrapper *around* the deck that
|
|
133
|
+
* matches `next:` is a page match, and an unbounded walk from a press on a slide would climb into
|
|
134
|
+
* it and step the deck on every press. Each press is handled by exactly one of the two.
|
|
96
135
|
*
|
|
97
136
|
* Exported for `effects/carousel` alongside `countSteps`: `next:`/`prev:`/`jump:` mean the same
|
|
98
137
|
* 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
|
|
138
|
+
* per-press re-query, the containment at the host) was found once and should not be found again.
|
|
100
139
|
*
|
|
101
|
-
* @returns The teardown for
|
|
102
|
-
* @complexity O(g × (m + d)) per click —
|
|
140
|
+
* @returns The teardown for both listeners.
|
|
141
|
+
* @complexity O(g × (m + d)) per click — at most two queries and one ancestor walk per named group,
|
|
103
142
|
* in that group's matches `m` and the pressed node's depth `d`; O(m) space for the largest group.
|
|
104
143
|
* @overallScore 100
|
|
105
144
|
*/
|
|
106
145
|
export declare function delegateControls(request: {
|
|
107
146
|
el: Element;
|
|
108
147
|
ctx: PrepareContext;
|
|
109
|
-
scope: TargetScope;
|
|
110
148
|
groups: ControlGroup[];
|
|
111
149
|
}): Cleanup;
|
|
112
150
|
/**
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Cleanup } from '../core/types.js';
|
|
2
|
+
import type { Direction } from '../core/gesture.js';
|
|
3
|
+
/**
|
|
4
|
+
* How a swipe reaches a deck it does not sit on.
|
|
5
|
+
*
|
|
6
|
+
* A swipe either shares the deck's element (`data-kui="swipe-y, carousel"` — the two write
|
|
7
|
+
* different attributes, so the channel check lets them compose) or sits on a wrapper around it.
|
|
8
|
+
* Either way the last step — "a left flick means next" — was left to every page, as a
|
|
9
|
+
* `MutationObserver` on `data-kui-swipe` that each one wrote slightly differently.
|
|
10
|
+
*
|
|
11
|
+
* ## Why an event from the swipe, heard by the deck
|
|
12
|
+
*
|
|
13
|
+
* The two alternatives both put the knowledge in the wrong place:
|
|
14
|
+
*
|
|
15
|
+
* - **The swipe steps the deck.** `swipeable` would have to find decks below it and call into
|
|
16
|
+
* their index, which makes the gesture primitive know what a carousel is, and makes every future
|
|
17
|
+
* stepper (a tab set, a gallery) an edit to the gesture module.
|
|
18
|
+
* - **A deck parameter naming its swipe** (`swipe:'.shell'`). A second selector to keep in sync
|
|
19
|
+
* with markup that already says it — the deck is *inside* the swipe — and a deck and its wrapper
|
|
20
|
+
* cannot drift apart if nothing names the other.
|
|
21
|
+
*
|
|
22
|
+
* An event inverts it cleanly: the swipe announces "left, here" and knows nothing about decks; a
|
|
23
|
+
* deck listens and decides whether the swipe was about it. It bubbles, so a page can still hear it
|
|
24
|
+
* without a `MutationObserver`, and the attribute stays exactly as it was for CSS.
|
|
25
|
+
*
|
|
26
|
+
* The deck listens on its document rather than on an ancestor because it cannot know which ancestor
|
|
27
|
+
* will be the swipe, and because the two start in either order — a listener on the document is
|
|
28
|
+
* already there whichever element's effect prepares first.
|
|
29
|
+
*/
|
|
30
|
+
/** The event a swipe dispatches on its own element. `detail.direction` is the flick. */
|
|
31
|
+
export declare const SWIPE_EVENT = "kui:swipe";
|
|
32
|
+
/** The payload of {@link SWIPE_EVENT}. */
|
|
33
|
+
export interface SwipeDetail {
|
|
34
|
+
direction: Direction;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Announce a recognised swipe from `el`.
|
|
38
|
+
*
|
|
39
|
+
* Built with the element's own realm's `CustomEvent`, so a deck in an iframe hears a swipe
|
|
40
|
+
* dispatched there.
|
|
41
|
+
*
|
|
42
|
+
* @complexity O(depth) for the bubble; O(1) space.
|
|
43
|
+
*/
|
|
44
|
+
export declare function announceSwipe(win: Window, el: Element, direction: Direction): void;
|
|
45
|
+
/**
|
|
46
|
+
* Which way a swipe moves a deck: the content follows the finger, as on any native pager.
|
|
47
|
+
*
|
|
48
|
+
* A flick left or up drags the next slide in (`1`); right or down brings the previous one back
|
|
49
|
+
* (`-1`). Pure and exported so the mapping is assertable without a gesture.
|
|
50
|
+
*
|
|
51
|
+
* @complexity O(1) time and space.
|
|
52
|
+
*/
|
|
53
|
+
export declare function swipeStep(direction: Direction): 1 | -1;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a swipe announced by `source` is about the deck `el`.
|
|
56
|
+
*
|
|
57
|
+
* It is when the swipe is on the deck itself, or contains it with no other deck between them. The
|
|
58
|
+
* self case is checked first because the walk below starts at the deck's parent and so would never
|
|
59
|
+
* meet a swipe on the deck: it would climb to the root and refuse at any outer deck. The second half is
|
|
60
|
+
* what keeps a swipe on a page of decks-within-decks from stepping every level at once: an inner
|
|
61
|
+
* deck (a gallery inside one slide of an outer one) is reached by its outer deck first, and the
|
|
62
|
+
* outer deck is the one the wrapper was put around. A deck is recognised by `data-kui-step`, the
|
|
63
|
+
* attribute every stepper in this library publishes on its own host.
|
|
64
|
+
*
|
|
65
|
+
* @complexity O(d) in the depth between the deck and the swipe; O(1) space.
|
|
66
|
+
*/
|
|
67
|
+
export declare function swipeReaches(source: Element, el: Element): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Step a deck whenever a swipe around it is announced.
|
|
70
|
+
*
|
|
71
|
+
* @param step - Called with `1` for next and `-1` for previous.
|
|
72
|
+
* @returns Removes the listener.
|
|
73
|
+
* @complexity O(1) to install; O(d) per swipe (see {@link swipeReaches}).
|
|
74
|
+
*/
|
|
75
|
+
export declare function stepOnSwipe(doc: Document, el: Element, step: (direction: 1 | -1) => void): Cleanup;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Animator } from './core/animator.js';
|
|
2
2
|
import type { AnimatorOptions } from './core/animator.js';
|
|
3
|
+
import type { DescribedStep, EffectDescription, ElementDescription, ParamNotes } from './core/describe.js';
|
|
4
|
+
import type { Registry } from './core/registry.js';
|
|
3
5
|
export * from './core/index.js';
|
|
4
6
|
export { COMBOS, PRESETS, PRIMITIVES, createRegistry } from './effects/index.js';
|
|
5
7
|
/**
|
|
@@ -14,3 +16,27 @@ export { COMBOS, PRESETS, PRIMITIVES, createRegistry } from './effects/index.js'
|
|
|
14
16
|
*/
|
|
15
17
|
export declare function kuinetic(options?: AnimatorOptions): Animator;
|
|
16
18
|
export default kuinetic;
|
|
19
|
+
export interface DescribeOptions {
|
|
20
|
+
/** Defaults to the bundled catalog. Pass `window.__kuinetic.registry` to include tier effects. */
|
|
21
|
+
registry?: Registry;
|
|
22
|
+
/** The plain-words table from the separate notes entry (`kuineticNotes.PARAM_NOTES`). */
|
|
23
|
+
notes?: ParamNotes;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Every parameter a reader could write for one effect name — see `core/describe.ts`.
|
|
27
|
+
*
|
|
28
|
+
* kuinetic.describe('hover-intent', { notes: kuineticNotes.PARAM_NOTES })
|
|
29
|
+
*
|
|
30
|
+
* @returns `undefined` when the name is not registered.
|
|
31
|
+
*/
|
|
32
|
+
export declare function describe(name: string, options?: DescribeOptions): EffectDescription | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* {@link describe} for every step of a whole `data-kui` value, parsed exactly as the runtime
|
|
35
|
+
* parses it: `describeSteps('fade-up 600ms, lift')`. Each step also says what it `written`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function describeSteps(value: string, options?: DescribeOptions): DescribedStep[];
|
|
38
|
+
/**
|
|
39
|
+
* {@link describeSteps}, plus the reserved keys (`on:`, `timeline:`, `at:` …) and which of the
|
|
40
|
+
* element-wide ones this value sets: `describeElement('fade-up on:click, lift')`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function describeElement(value: string, options?: DescribeOptions): ElementDescription;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ParamNotes } from '../core/describe.js';
|
|
2
|
+
/**
|
|
3
|
+
* The plain-words note for every parameter in the bundled catalog, read by `describe()`.
|
|
4
|
+
*
|
|
5
|
+
* A separate entry (`kuinetic/notes`, and `kuinetic.notes.js` for a script tag) rather than a
|
|
6
|
+
* field on each `ParamSpec`, because this is prose only documentation pages read: inline, it would
|
|
7
|
+
* ship to every visitor of every site using the library. It imports types only, so it carries no
|
|
8
|
+
* runtime code either.
|
|
9
|
+
*
|
|
10
|
+
* Kept in step with the registry by `test/param-notes.test.ts`: every registered parameter must
|
|
11
|
+
* resolve a note, and every key here must name a parameter that exists.
|
|
12
|
+
*/
|
|
13
|
+
export declare const PARAM_NOTES: ParamNotes;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ParamNotes } from '../core/describe.js';
|
|
2
|
+
/**
|
|
3
|
+
* `effects/catalog/interaction.ts`, `interaction-reveal.ts` (all but hover-intent, noted in
|
|
4
|
+
* `notes/interaction-reveal.ts`), `interaction-states.ts` and `interaction-proximity.ts`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const INTERACTION_NOTES: ParamNotes;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ParamNotes } from '../core/describe.js';
|
|
2
|
+
/**
|
|
3
|
+
* The reserved `data-kui` keys — the ones the parser keeps for itself rather than handing to an
|
|
4
|
+
* effect. Keyed `'data-kui.<key>'` (`KEY_NOTE_OWNER` in `core/describe.ts`), so no effect name can
|
|
5
|
+
* shadow them. The first block applies to the whole element; the second to the step it is in.
|
|
6
|
+
*/
|
|
7
|
+
export declare const KEY_NOTES: ParamNotes;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ParamNotes } from '../core/describe.js';
|
|
2
|
+
/**
|
|
3
|
+
* `effects/layout/primitives.ts`, `effects/three-d/index.ts`, `effects/gestures/primitives.ts`,
|
|
4
|
+
* `effects/svg/index.ts`, `effects/motion-path/index.ts`, `effects/tween/*` and the `blur`, `scale`,
|
|
5
|
+
* `rotate`, `skew`, `progress` primitives in `effects/catalog/core.ts`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MECHANICS_NOTES: ParamNotes;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ParamNotes } from '../core/describe.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parameters that mean the same thing on every primitive that declares them. A primitive whose
|
|
4
|
+
* `duration` means something else (one cycle of a loop, say) overrides it under its own key.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SHARED_NOTES: ParamNotes;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ParamNotes } from '../core/describe.js';
|
|
2
|
+
/**
|
|
3
|
+
* `showcase/*` (compare, device-frame, hotspots, lightbox, scroll-story, slideshow, slow-mo) and
|
|
4
|
+
* the step decks in `effects/carousel/*` and `effects/step-index.ts` (spatial-ring, spatial-stack,
|
|
5
|
+
* step-progress).
|
|
6
|
+
*/
|
|
7
|
+
export declare const SHOWCASE_NOTES: ParamNotes;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { Preset, Primitive } from '../core/types.js';
|
|
2
2
|
export declare const COMPARE_AXIS_ATTR = "data-kui-compare-axis";
|
|
3
|
+
/** Stamped only on a frame of three or more media: the number of dividers. Absent for a pair. */
|
|
4
|
+
export declare const COMPARE_DIVIDERS_ATTR = "data-kui-compare-dividers";
|
|
5
|
+
/** Below this a sweep is a flicker between the two images, not a comparison. */
|
|
6
|
+
export declare const MIN_SWEEP_MS = 250;
|
|
7
|
+
/**
|
|
8
|
+
* The longest step one frame may take. A tab returning from the background hands the next frame a
|
|
9
|
+
* gap of however long it was hidden; integrating that whole gap would jump the divider.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_FRAME_MS = 100;
|
|
3
12
|
export declare const COMPARE_PRIMITIVE: Primitive;
|
|
4
13
|
export declare const COMPARE_PRESETS: Preset[];
|
|
@@ -23,6 +23,9 @@ export declare const SHOWCASE_PRESETS: Preset[];
|
|
|
23
23
|
* `kuinetic.showcase.js` tag will hand a page's existing animator tomorrow. See
|
|
24
24
|
* `src/core/register-into.ts`'s docblock for why identity cannot survive that split and shape can.
|
|
25
25
|
*
|
|
26
|
+
* Also provides the deck viewer (`core/deck-viewer.ts`), the one capability decks in `src/effects/`
|
|
27
|
+
* reach in this module without importing it.
|
|
28
|
+
*
|
|
26
29
|
* @param target - The registry, the animator, or a host exposing one as `.registry`.
|
|
27
30
|
* @returns `target`, so callers can chain.
|
|
28
31
|
* @complexity O(n) time in showcase primitives plus presets; O(1) extra space.
|