kuinetic 0.2.1 → 0.2.2

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/dist/kuinetic.js CHANGED
@@ -589,18 +589,25 @@ var kuinetic = (() => {
589
589
  if (typeof IntersectionObserver === "undefined") return void 0;
590
590
  return (callback, init) => new IntersectionObserver(callback, init);
591
591
  }
592
+ function onlyBoundLate(name, claims) {
593
+ const authorising = authorisingActivations(name);
594
+ if (!authorising.includes("enter")) return false;
595
+ const suits = (claim2) => authorising.some((named) => claim2.supported.includes(named));
596
+ if (!claims.some((claim2) => claim2.entrance && suits(claim2))) return false;
597
+ return claims.every((claim2) => suits(claim2) || claim2.supported.includes("load"));
598
+ }
592
599
  function warnAboutActivation(request) {
593
600
  warnUnsupported(request);
594
601
  warnUnknownEvents(request);
595
602
  }
596
- function warnUnsupported({ el, spec, supported, reporter }) {
597
- if (supported.length === 0) return;
603
+ function warnUnsupported({ el, spec, supported, claims, reporter }) {
604
+ const declaredAny = supported.length > 0 || (claims?.some((claim2) => claim2.supported.length > 0) ?? false);
605
+ if (!declaredAny) return;
606
+ const shared = supported.length > 0 ? supported.join(", ") : "none in common across the composed effects";
598
607
  for (const name of spec.names) {
599
608
  if (authorisingActivations(name).some((named) => supported.includes(named))) continue;
600
- reporter.warn(
601
- `activation "${name}" is not supported by this effect (supports: ${supported.join(", ")})`,
602
- el
603
- );
609
+ if (claims && onlyBoundLate(name, claims)) continue;
610
+ reporter.warn(`activation "${name}" is not supported by this effect (supports: ${shared})`, el);
604
611
  }
605
612
  }
606
613
  function warnUnknownEvents({ el, spec, reporter }) {
@@ -3427,6 +3434,7 @@ var kuinetic = (() => {
3427
3434
  unknown,
3428
3435
  reducedMotion: "shorten",
3429
3436
  supportedActivations: [],
3437
+ activationClaims: [],
3430
3438
  supportedTimelines: [],
3431
3439
  channels: [],
3432
3440
  warnings
@@ -3560,6 +3568,10 @@ var kuinetic = (() => {
3560
3568
  plan.fxNames.push(preset.name);
3561
3569
  plan.reducedMotion = strictestPolicy(plan.reducedMotion, primitive.reducedMotion);
3562
3570
  activations = intersect(activations, primitive.supportedActivations);
3571
+ plan.activationClaims.push({
3572
+ supported: primitive.supportedActivations,
3573
+ entrance: phaseOf(entry) === "entrance"
3574
+ });
3563
3575
  timelines = intersect(timelines, primitive.supportedTimelines);
3564
3576
  for (const channel of channelsFor(entry)) channels.add(channel);
3565
3577
  warnUnsupportedTimeline(preset.name, primitive.supportedTimelines, timeline, warnings);
@@ -5125,11 +5137,20 @@ var kuinetic = (() => {
5125
5137
  const windowRoot = windowScrollRoot(win);
5126
5138
  return (el) => {
5127
5139
  for (let node = el.parentElement; node; node = node.parentElement) {
5140
+ if (scrollsTheViewport(node, win)) return windowRoot;
5128
5141
  if (isScrollable(node)) return elementScrollRoot(node, win);
5129
5142
  }
5130
5143
  return windowRoot;
5131
5144
  };
5132
5145
  }
5146
+ function scrollsTheViewport(node, win) {
5147
+ const root = win.document.documentElement;
5148
+ if (node === root) return true;
5149
+ if (node !== win.document.body) return false;
5150
+ const rootStyle = win.getComputedStyle(root);
5151
+ return !CLIPPING_OVERFLOW.has(rootStyle.overflowX) && !CLIPPING_OVERFLOW.has(rootStyle.overflowY);
5152
+ }
5153
+ var CLIPPING_OVERFLOW = /* @__PURE__ */ new Set(["auto", "scroll", "overlay", "hidden", "clip"]);
5133
5154
  var SCROLLABLE_OVERFLOW = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
5134
5155
  function defaultIsScrollable(win) {
5135
5156
  return (el) => {
@@ -5632,6 +5653,7 @@ var kuinetic = (() => {
5632
5653
  el,
5633
5654
  spec: resolveActivationSpec(config.activation),
5634
5655
  supported: plan.supportedActivations,
5656
+ claims: plan.activationClaims,
5635
5657
  reporter: this.reporter
5636
5658
  });
5637
5659
  return config.activation;
@@ -11146,6 +11168,10 @@ var kuinetic = (() => {
11146
11168
  let active = false;
11147
11169
  let longPressTimer = null;
11148
11170
  let longPressFired = false;
11171
+ let captured = false;
11172
+ let pointerId = null;
11173
+ let lastHandled = null;
11174
+ let documentListening = null;
11149
11175
  function sampleOf(event) {
11150
11176
  return { x: event.clientX, y: event.clientY, time: deps.now() };
11151
11177
  }
@@ -11163,7 +11189,9 @@ var kuinetic = (() => {
11163
11189
  samples = [origin];
11164
11190
  active = false;
11165
11191
  longPressFired = false;
11166
- if (capturePointer) el.setPointerCapture?.(event.pointerId);
11192
+ pointerId = event.pointerId;
11193
+ captured = capturePointer === true && takeCapture(event.pointerId);
11194
+ if (!captured) listenOnDocument();
11167
11195
  if (longPressMs > 0) {
11168
11196
  longPressTimer = deps.setTimer(() => {
11169
11197
  longPressFired = true;
@@ -11171,8 +11199,43 @@ var kuinetic = (() => {
11171
11199
  }, longPressMs);
11172
11200
  }
11173
11201
  }
11202
+ function takeCapture(id) {
11203
+ if (typeof el.setPointerCapture !== "function") return false;
11204
+ try {
11205
+ el.setPointerCapture(id);
11206
+ return true;
11207
+ } catch {
11208
+ return false;
11209
+ }
11210
+ }
11211
+ function captureOnDrag(event) {
11212
+ if (capturePointer !== "drag") return;
11213
+ captured = takeCapture(event.pointerId);
11214
+ if (captured) stopListeningOnDocument();
11215
+ }
11216
+ function listenOnDocument() {
11217
+ const doc = el.ownerDocument;
11218
+ if (!doc || documentListening) return;
11219
+ documentListening = doc;
11220
+ doc.addEventListener("pointermove", onMove, { passive: true });
11221
+ doc.addEventListener("pointerup", onUp, { passive: true });
11222
+ doc.addEventListener("pointercancel", onUp, { passive: true });
11223
+ }
11224
+ function stopListeningOnDocument() {
11225
+ const doc = documentListening;
11226
+ if (!doc) return;
11227
+ documentListening = null;
11228
+ doc.removeEventListener("pointermove", onMove);
11229
+ doc.removeEventListener("pointerup", onUp);
11230
+ doc.removeEventListener("pointercancel", onUp);
11231
+ }
11232
+ function firstHearing(event) {
11233
+ if (event === lastHandled) return false;
11234
+ lastHandled = event;
11235
+ return true;
11236
+ }
11174
11237
  function onMove(event) {
11175
- if (!origin) return;
11238
+ if (!origin || !firstHearing(event)) return;
11176
11239
  const sample = sampleOf(event);
11177
11240
  samples.push(sample);
11178
11241
  if (samples.length > MAX_SAMPLES2) samples.shift();
@@ -11181,17 +11244,17 @@ var kuinetic = (() => {
11181
11244
  if (!active) {
11182
11245
  active = true;
11183
11246
  clearLongPress();
11247
+ captureOnDrag(event);
11184
11248
  handlers.onStart?.(origin);
11185
11249
  }
11186
11250
  handlers.onMove?.(vector, sample);
11187
11251
  }
11188
- function onUp(event) {
11189
- clearLongPress();
11190
- if (capturePointer) el.releasePointerCapture?.(event.pointerId);
11191
- if (!origin) return;
11192
- const sample = sampleOf(event);
11193
- samples.push(sample);
11194
- const vector = vectorNow(sample);
11252
+ function releaseCapture(id) {
11253
+ if (captured) el.releasePointerCapture?.(id);
11254
+ captured = false;
11255
+ pointerId = null;
11256
+ }
11257
+ function reportEnd(vector, sample) {
11195
11258
  if (active) {
11196
11259
  handlers.onEnd?.(vector, sample);
11197
11260
  const direction = swipeDirection(vector, swipeVelocity);
@@ -11199,21 +11262,38 @@ var kuinetic = (() => {
11199
11262
  } else if (longPressFired) {
11200
11263
  handlers.onEnd?.(vector, sample);
11201
11264
  }
11265
+ }
11266
+ function onUp(event) {
11267
+ clearLongPress();
11268
+ stopListeningOnDocument();
11269
+ releaseCapture(event.pointerId);
11270
+ if (!origin) return;
11271
+ const sample = sampleOf(event);
11272
+ samples.push(sample);
11273
+ reportEnd(vectorNow(sample), sample);
11202
11274
  origin = null;
11203
11275
  active = false;
11204
11276
  longPressFired = false;
11205
11277
  samples = [];
11206
11278
  }
11279
+ function onLostCapture(event) {
11280
+ if (!captured || event.pointerId !== pointerId) return;
11281
+ captured = false;
11282
+ onUp(event);
11283
+ }
11207
11284
  el.addEventListener("pointerdown", onDown);
11208
11285
  el.addEventListener("pointermove", onMove, { passive: true });
11209
11286
  el.addEventListener("pointerup", onUp, { passive: true });
11210
11287
  el.addEventListener("pointercancel", onUp, { passive: true });
11288
+ el.addEventListener("lostpointercapture", onLostCapture, { passive: true });
11211
11289
  return () => {
11212
11290
  clearLongPress();
11291
+ stopListeningOnDocument();
11213
11292
  el.removeEventListener("pointerdown", onDown);
11214
11293
  el.removeEventListener("pointermove", onMove);
11215
11294
  el.removeEventListener("pointerup", onUp);
11216
11295
  el.removeEventListener("pointercancel", onUp);
11296
+ el.removeEventListener("lostpointercapture", onLostCapture);
11217
11297
  };
11218
11298
  }
11219
11299
  function defaultGestureDeps() {
@@ -11362,11 +11442,12 @@ var kuinetic = (() => {
11362
11442
  {
11363
11443
  axis: params.text("axis", "both"),
11364
11444
  swipeVelocity: params.num("velocity", 300),
11365
- // This primitive publishes an attribute and moves nothing, so it has no reason to hold the
11366
- // pointer — and holding it retargets the following `click` at this element, which silently
11367
- // breaks every interactive child. A `swipe-x` on a carousel shell killed its own dots and
11368
- // buttons that way. See `capturePointer` in `core/gesture.ts`.
11369
- capturePointer: false
11445
+ // This primitive moves nothing, so it must not hold the pointer from `pointerdown` — that
11446
+ // retargets the following `click` at this element and kills every interactive child (a
11447
+ // `swipe-x` on a carousel shell killed its own dots and buttons). But a flick that leaves a
11448
+ // small element never delivers `pointerup` to it unless captured, so capture only once the
11449
+ // drag starts. See `capturePointer` in `core/gesture.ts`.
11450
+ capturePointer: "drag"
11370
11451
  }
11371
11452
  );
11372
11453
  return () => {
@@ -15440,7 +15521,7 @@ var kuinetic = (() => {
15440
15521
  return recognise(el, { onSwipe: (direction) => {
15441
15522
  if (direction === "left") index.next();
15442
15523
  if (direction === "right") index.prev();
15443
- } }, { axis: "x", capturePointer: false });
15524
+ } }, { axis: "x", capturePointer: "drag" });
15444
15525
  }
15445
15526
  function prepareSlideshow(el, params, ctx) {
15446
15527
  const selector = resolveTarget(
@@ -15909,17 +15990,24 @@ var __kuineticBoot = (() => {
15909
15990
  }
15910
15991
  }
15911
15992
  function guarded(runtime, original, core, options) {
15993
+ const wantsShared = !options || Object.entries(options).every(([key, value]) => key === "observe" && value === true);
15994
+ if (runtime.manual) {
15995
+ const made = original.call(core, options);
15996
+ adoptManual(runtime, made);
15997
+ return made;
15998
+ }
15912
15999
  if (!runtime.started) {
15913
16000
  const replacement = original.call(core, options);
15914
16001
  adopt(runtime, replacement);
15915
- warnManualCall(
15916
- runtime,
15917
- "yours is now the one this page uses, exactly as it would have been before the script tag started one, so the call is safe to delete"
15918
- );
16002
+ if (wantsShared) {
16003
+ warnManualCall(
16004
+ runtime,
16005
+ "yours is now the one this page uses, exactly as it would have been before the script tag started one, so the call is safe to delete"
16006
+ );
16007
+ }
15919
16008
  return replacement;
15920
16009
  }
15921
16010
  const shared = runtime.animator;
15922
- const wantsShared = !options || Object.entries(options).every(([key, value]) => key === "observe" && value === true);
15923
16011
  if (shared && wantsShared) {
15924
16012
  warnManualCall(runtime, "you have been handed the one it already made, so this call is safe to delete");
15925
16013
  return shared;
@@ -15946,6 +16034,40 @@ var __kuineticBoot = (() => {
15946
16034
  runtime.animator = animator;
15947
16035
  globalScope().__kuinetic = animator;
15948
16036
  }
16037
+ function adoptManual(runtime, animator) {
16038
+ if (!runtime.animator) adopt(runtime, animator);
16039
+ const entry = { animator, started: false };
16040
+ const list = runtime.handBuilt ??= [];
16041
+ list.push(entry);
16042
+ const start = animator.start.bind(animator);
16043
+ animator.start = () => {
16044
+ entry.started = true;
16045
+ return start();
16046
+ };
16047
+ if (typeof animator.destroy === "function") {
16048
+ const destroy = animator.destroy.bind(animator);
16049
+ animator.destroy = () => {
16050
+ const at = list.indexOf(entry);
16051
+ if (at >= 0) list.splice(at, 1);
16052
+ return destroy();
16053
+ };
16054
+ }
16055
+ for (const tier of runtime.pending) registerTier(tier, animator);
16056
+ }
16057
+ function registerTier(entry, animator) {
16058
+ try {
16059
+ entry.register(animator);
16060
+ } catch (error) {
16061
+ warn(`the "${entry.tier}" tier failed to register: ${String(error)}`);
16062
+ }
16063
+ }
16064
+ function enlistOnManual(runtime, entry) {
16065
+ runtime.pending.push(entry);
16066
+ for (const { animator, started } of [...runtime.handBuilt ?? []]) {
16067
+ registerTier(entry, animator);
16068
+ if (started) recompile(animator);
16069
+ }
16070
+ }
15949
16071
  function createAnimator(runtime, core) {
15950
16072
  adopt(runtime, core.kuinetic({ observe: true }));
15951
16073
  }
@@ -15975,13 +16097,7 @@ var __kuineticBoot = (() => {
15975
16097
  }
15976
16098
  function drain(runtime, animator) {
15977
16099
  const pending = runtime.pending.splice(0, runtime.pending.length);
15978
- for (const entry of pending) {
15979
- try {
15980
- entry.register(animator);
15981
- } catch (error) {
15982
- warn(`the "${entry.tier}" tier failed to register: ${String(error)}`);
15983
- }
15984
- }
16100
+ for (const entry of pending) registerTier(entry, animator);
15985
16101
  return pending.length;
15986
16102
  }
15987
16103
  function runBoot(runtime) {
@@ -16003,13 +16119,6 @@ var __kuineticBoot = (() => {
16003
16119
  }
16004
16120
  if (registered) recompile(animator);
16005
16121
  }
16006
- function optedOut(runtime, options) {
16007
- if (taggedManual()) {
16008
- if (options.core) runtime.manual = true;
16009
- return true;
16010
- }
16011
- return runtime.manual;
16012
- }
16013
16122
  function enlist(runtime, options) {
16014
16123
  if (options.core) {
16015
16124
  if (runtime.animator) return;
@@ -16023,7 +16132,17 @@ var __kuineticBoot = (() => {
16023
16132
  if (typeof document === "undefined" || typeof globalThis === "undefined") return;
16024
16133
  const runtime = getRuntime();
16025
16134
  checkVersion(runtime, options.tier);
16026
- if (optedOut(runtime, options)) return;
16135
+ if (taggedManual()) {
16136
+ if (options.core) {
16137
+ runtime.manual = true;
16138
+ installGuard(runtime, options.core, options.globalName);
16139
+ }
16140
+ return;
16141
+ }
16142
+ if (runtime.manual) {
16143
+ if (options.register) enlistOnManual(runtime, { tier: options.tier, register: options.register });
16144
+ return;
16145
+ }
16027
16146
  enlist(runtime, options);
16028
16147
  schedule(runtime);
16029
16148
  }