motion 14.4.0 → 14.5.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.
@@ -6136,6 +6136,43 @@
6136
6136
  return isObject(element) && "ownerSVGElement" in element;
6137
6137
  }
6138
6138
 
6139
+ const activeScope = {};
6140
+ /**
6141
+ * Register `cleanup` with the active scope, if there is one, so stopping
6142
+ * the scope also runs it.
6143
+ */
6144
+ function addToScope(cleanup) {
6145
+ activeScope.current?.add(cleanup);
6146
+ return cleanup;
6147
+ }
6148
+ /**
6149
+ * Collect the animation a builder like `animateLayout()` or
6150
+ * `animateView()` resolves with. If the scope stops first, the animation
6151
+ * is stopped as soon as it starts.
6152
+ */
6153
+ function addBuilderToScope(builder) {
6154
+ const scope = activeScope.current;
6155
+ if (!scope)
6156
+ return;
6157
+ let isStopped = false;
6158
+ let stop;
6159
+ const remove = scope.add(() => {
6160
+ isStopped = true;
6161
+ stop?.();
6162
+ });
6163
+ builder.then((animation) => {
6164
+ if (!animation)
6165
+ return;
6166
+ stop = () => animation.stop();
6167
+ isStopped ? stop() : animation.finished.then(remove);
6168
+ }, remove);
6169
+ }
6170
+ /**
6171
+ * Bind `callback` to the active scope, so anything it starts later (for
6172
+ * instance an animation in an `inView()` callback) is collected too.
6173
+ */
6174
+ const bindToScope = (callback) => activeScope.current ? activeScope.current.bind(callback) : callback;
6175
+
6139
6176
  function resolveElements(elementOrSelector, scope, selectorCache) {
6140
6177
  if (elementOrSelector == null) {
6141
6178
  return [];
@@ -6144,10 +6181,9 @@
6144
6181
  return [elementOrSelector];
6145
6182
  }
6146
6183
  else if (typeof elementOrSelector === "string") {
6147
- let root = document;
6148
- if (scope) {
6149
- root = scope.current;
6150
- }
6184
+ const root = scope
6185
+ ? scope.current
6186
+ : activeScope.current?.root || document;
6151
6187
  const elements = selectorCache?.[elementOrSelector] ??
6152
6188
  root.querySelectorAll(elementOrSelector);
6153
6189
  return elements ? Array.from(elements) : [];
@@ -6937,12 +6973,20 @@
6937
6973
  * sharing values and a renderer.
6938
6974
  */
6939
6975
  function animateElement(element, keyframes, transition, visualElement) {
6976
+ activeScope.current?.track(element);
6940
6977
  if (visualElement) {
6941
6978
  return animateValues((key) => visualElement.getValue(key, null), keyframes, transition, visualElement);
6942
6979
  }
6943
6980
  const effect = getElementEffect(element);
6944
6981
  return animateEffectSubject(effect, element, keyframes, transition, new EffectSubject(effect, element));
6945
6982
  }
6983
+ /**
6984
+ * Detach the values styleEffect/svgEffect bound to `element`, so the
6985
+ * next animation reads its starting values from the DOM again.
6986
+ */
6987
+ const releaseElement = (element) => getElementEffect(element)
6988
+ .state(element)
6989
+ ?.release();
6946
6990
  /**
6947
6991
  * Hand the values styleEffect/svgEffect are rendering on `element`,
6948
6992
  * whether bound by animate() or directly, to a VisualElement that now
@@ -7026,6 +7070,7 @@
7026
7070
  */
7027
7071
  function delay(callback, timeout) {
7028
7072
  const start = time.now();
7073
+ callback = bindToScope(callback);
7029
7074
  const checkElapsed = ({ timestamp }) => {
7030
7075
  const elapsed = timestamp - start;
7031
7076
  if (elapsed >= timeout) {
@@ -7034,7 +7079,7 @@
7034
7079
  }
7035
7080
  };
7036
7081
  frame.setup(checkElapsed, true);
7037
- return () => cancelFrame(checkElapsed);
7082
+ return addToScope(() => cancelFrame(checkElapsed));
7038
7083
  }
7039
7084
  function delayInSeconds(callback, timeout) {
7040
7085
  return delay(callback, secondsToMilliseconds(timeout));
@@ -10455,7 +10500,7 @@
10455
10500
  function parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options) {
10456
10501
  if (typeof scopeOrUpdateDom === "function") {
10457
10502
  return {
10458
- scope: document,
10503
+ scope: activeScope.current?.root || document,
10459
10504
  updateDom: scopeOrUpdateDom,
10460
10505
  defaultOptions: updateDomOrOptions,
10461
10506
  };
@@ -10591,7 +10636,7 @@
10591
10636
  }
10592
10637
 
10593
10638
  function resize(a, b) {
10594
- return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
10639
+ return addToScope(typeof a === "function" ? resizeWindow(a) : resizeElement(a, b));
10595
10640
  }
10596
10641
 
10597
10642
  const distance = (a, b) => Math.abs(a - b);
@@ -10656,6 +10701,12 @@
10656
10701
  onMove && onMove(this.lastMoveEvent, info);
10657
10702
  };
10658
10703
  this.handlePointerMove = (event, info) => {
10704
+ /**
10705
+ * This event's pageX/pageY already include any window scroll that
10706
+ * hasn't fired its scroll event yet. Settle it now, so that scroll
10707
+ * event doesn't add it to the new point a second time.
10708
+ */
10709
+ this.handleScroll(window);
10659
10710
  this.lastMoveEvent = event;
10660
10711
  this.lastRawMoveEventInfo = info;
10661
10712
  this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint);
@@ -11687,7 +11738,20 @@
11687
11738
  */
11688
11739
  function drag(elementOrSelector, options = {}) {
11689
11740
  const props = toProps(options);
11741
+ const scope = activeScope.current;
11742
+ /**
11743
+ * Run callbacks in the active scope, so work they start is collected.
11744
+ */
11745
+ if (scope) {
11746
+ for (const key in props) {
11747
+ const value = props[key];
11748
+ if (typeof value === "function") {
11749
+ props[key] = scope.bind(value);
11750
+ }
11751
+ }
11752
+ }
11690
11753
  const cleanups = resolveElements(elementOrSelector).map((element) => {
11754
+ scope?.track(element);
11691
11755
  const [node, release] = acquireProjectionNode(element);
11692
11756
  /**
11693
11757
  * Measure layout even when not animating it, as constraints and
@@ -11701,10 +11765,11 @@
11701
11765
  removeStyles();
11702
11766
  removeListeners?.();
11703
11767
  controls.cancel();
11768
+ controls.stopAnimation();
11704
11769
  release();
11705
11770
  };
11706
11771
  });
11707
- return () => cleanups.forEach((cleanup) => cleanup());
11772
+ return addToScope(() => cleanups.forEach((cleanup) => cleanup()));
11708
11773
  }
11709
11774
 
11710
11775
  function setupGesture(elementOrSelector, options) {
@@ -11715,7 +11780,7 @@
11715
11780
  ...options,
11716
11781
  signal: gestureAbortController.signal,
11717
11782
  };
11718
- const cancel = () => gestureAbortController.abort();
11783
+ const cancel = addToScope(() => gestureAbortController.abort());
11719
11784
  return [elements, eventOptions, cancel];
11720
11785
  }
11721
11786
 
@@ -11730,6 +11795,7 @@
11730
11795
  * @public
11731
11796
  */
11732
11797
  function hover(elementOrSelector, onHoverStart, options = {}) {
11798
+ onHoverStart = bindToScope(onHoverStart);
11733
11799
  const [elements, eventOptions, cancel] = setupGesture(elementOrSelector, options);
11734
11800
  elements.forEach((element) => {
11735
11801
  let isPressed = false;
@@ -11818,26 +11884,36 @@
11818
11884
  function firePointerEvent(target, type) {
11819
11885
  target.dispatchEvent(new PointerEvent("pointer" + type, { isPrimary: true, bubbles: true }));
11820
11886
  }
11821
- const enableKeyboardPress = (focusEvent, eventOptions) => {
11822
- const element = focusEvent.currentTarget;
11823
- if (!element)
11824
- return;
11887
+ const enableKeyboardPress = (element, eventOptions) => {
11888
+ /**
11889
+ * Listeners are scoped to this focus and all released on blur, rather
11890
+ * than added per Enter press, so they can't accumulate and fire
11891
+ * duplicate pointerup/pointercancel events. See #3935.
11892
+ */
11893
+ let isKeyPressing = false;
11825
11894
  const handleKeydown = filterEvents(() => {
11826
11895
  if (isPressing.has(element))
11827
11896
  return;
11897
+ isKeyPressing = true;
11828
11898
  firePointerEvent(element, "down");
11829
- const handleKeyup = filterEvents(() => {
11830
- firePointerEvent(element, "up");
11831
- });
11832
- const handleBlur = () => firePointerEvent(element, "cancel");
11833
- element.addEventListener("keyup", handleKeyup, eventOptions);
11834
- element.addEventListener("blur", handleBlur, eventOptions);
11899
+ });
11900
+ const handleKeyup = filterEvents(() => {
11901
+ if (!isKeyPressing)
11902
+ return;
11903
+ isKeyPressing = false;
11904
+ firePointerEvent(element, "up");
11835
11905
  });
11836
11906
  element.addEventListener("keydown", handleKeydown, eventOptions);
11907
+ element.addEventListener("keyup", handleKeyup, eventOptions);
11837
11908
  /**
11838
- * Add an event listener that fires on blur to remove the keydown events.
11909
+ * On blur, remove the key listeners and cancel any keyboard press.
11839
11910
  */
11840
- element.addEventListener("blur", () => element.removeEventListener("keydown", handleKeydown), eventOptions);
11911
+ element.addEventListener("blur", () => {
11912
+ element.removeEventListener("keydown", handleKeydown);
11913
+ element.removeEventListener("keyup", handleKeyup);
11914
+ if (isKeyPressing)
11915
+ firePointerEvent(element, "cancel");
11916
+ }, { ...eventOptions, once: true });
11841
11917
  };
11842
11918
 
11843
11919
  /**
@@ -11868,6 +11944,7 @@
11868
11944
  * @public
11869
11945
  */
11870
11946
  function press(targetOrSelector, onPressStart, options = {}) {
11947
+ onPressStart = bindToScope(onPressStart);
11871
11948
  const [targets, eventOptions, cancelEvents] = setupGesture(targetOrSelector, options);
11872
11949
  const startPress = (startEvent) => {
11873
11950
  const target = startEvent.currentTarget;
@@ -11916,7 +11993,7 @@
11916
11993
  const pointerDownTarget = options.useGlobalTarget ? window : target;
11917
11994
  pointerDownTarget.addEventListener("pointerdown", startPress, eventOptions);
11918
11995
  if (isHTMLElement(target)) {
11919
- target.addEventListener("focus", (event) => enableKeyboardPress(event, eventOptions));
11996
+ target.addEventListener("focus", () => enableKeyboardPress(target, eventOptions), eventOptions);
11920
11997
  if (!isElementKeyboardAccessible(target) &&
11921
11998
  !target.hasAttribute("tabindex")) {
11922
11999
  target.tabIndex = 0;
@@ -11933,6 +12010,160 @@
11933
12010
  : computedStyle[name];
11934
12011
  }
11935
12012
 
12013
+ const keywords = {
12014
+ reducedMotion: "(prefers-reduced-motion: reduce)",
12015
+ motion: "(prefers-reduced-motion: no-preference)",
12016
+ dark: "(prefers-color-scheme: dark)",
12017
+ light: "(prefers-color-scheme: light)",
12018
+ hover: "(hover: hover) and (pointer: fine)",
12019
+ touch: "(pointer: coarse)",
12020
+ portrait: "(orientation: portrait)",
12021
+ landscape: "(orientation: landscape)",
12022
+ };
12023
+ /**
12024
+ * Turn a condition into a media query. Full media queries pass through
12025
+ * unchanged. Otherwise each space-separated token must match:
12026
+ *
12027
+ * - A width comparison: `">=800"`, `"<1200px"`, `"> 40em"`
12028
+ * - A keyword: `reducedMotion`, `motion`, `dark`, `light`, `hover`,
12029
+ * `touch`, `portrait`, `landscape`
12030
+ *
12031
+ * Prefix a token with `!` to negate it, for instance `">=800 !touch"`.
12032
+ */
12033
+ function toMediaQuery(condition) {
12034
+ if (condition.includes("("))
12035
+ return condition;
12036
+ return condition
12037
+ .replace(/([<>!]=?)\s+/g, "$1")
12038
+ .trim()
12039
+ .split(/\s+/)
12040
+ .map((token) => {
12041
+ const isNot = token[0] === "!";
12042
+ if (isNot)
12043
+ token = token.slice(1);
12044
+ const width = /^([<>]=?)([\d.]+)([a-z]*)$/.exec(token);
12045
+ const query = width
12046
+ ? `(width ${width[1]} ${width[2]}${width[3] || "px"})`
12047
+ : keywords[token] || token;
12048
+ return isNot ? `(not (${query}))` : query;
12049
+ })
12050
+ .join(" and ");
12051
+ }
12052
+
12053
+ function scopeMotion(...args) {
12054
+ const root = typeof args[0] === "function" ? undefined : args.shift();
12055
+ const [setup, options = {}] = args;
12056
+ const { transition, matchMedia } = options;
12057
+ const queries = [];
12058
+ const styles = new Map();
12059
+ let cleanups = [];
12060
+ let isPending = false;
12061
+ const active = {
12062
+ root: resolveElements(root)[0],
12063
+ transition,
12064
+ add(cleanup) {
12065
+ cleanups.push(cleanup);
12066
+ return () => removeItem(cleanups, cleanup);
12067
+ },
12068
+ bind: (callback) => ((...args) => {
12069
+ const result = scope.run(() => callback(...args));
12070
+ return typeof result === "function"
12071
+ ? active.bind(result)
12072
+ : result;
12073
+ }),
12074
+ track(element) {
12075
+ styles.has(element) ||
12076
+ styles.set(element, element.getAttribute("style"));
12077
+ },
12078
+ };
12079
+ const clear = (restore) => {
12080
+ const current = cleanups;
12081
+ cleanups = [];
12082
+ for (let i = current.length - 1; i >= 0; i--)
12083
+ current[i]();
12084
+ restore &&
12085
+ styles.forEach((style, element) => {
12086
+ releaseElement(element);
12087
+ style === null
12088
+ ? element.removeAttribute("style")
12089
+ : element.setAttribute("style", style);
12090
+ });
12091
+ styles.clear();
12092
+ };
12093
+ const start = () => {
12094
+ scope.state = "active";
12095
+ let matches = !queries.length;
12096
+ scope.media = {};
12097
+ for (const [name, query] of queries) {
12098
+ matches = (scope.media[name] = query.matches) || matches;
12099
+ }
12100
+ if (matches && setup) {
12101
+ const cleanup = scope.run(() => setup(scope));
12102
+ typeof cleanup === "function" && active.add(cleanup);
12103
+ }
12104
+ };
12105
+ const onChange = () => {
12106
+ if (isPending)
12107
+ return;
12108
+ isPending = true;
12109
+ /**
12110
+ * Several conditions can change together, for instance two
12111
+ * breakpoints crossed by one resize. Refresh once.
12112
+ */
12113
+ queueMicrotask(() => {
12114
+ isPending = false;
12115
+ queries.length && scope.refresh();
12116
+ });
12117
+ };
12118
+ const kill = (restore) => {
12119
+ scope.state = restore ? "cancelled" : "stopped";
12120
+ for (const [, query] of queries) {
12121
+ query.removeEventListener("change", onChange);
12122
+ }
12123
+ queries.length = 0;
12124
+ clear(restore);
12125
+ };
12126
+ const runIn = (target, callback) => {
12127
+ const prev = activeScope.current;
12128
+ activeScope.current = target;
12129
+ try {
12130
+ return callback();
12131
+ }
12132
+ finally {
12133
+ activeScope.current = prev;
12134
+ }
12135
+ };
12136
+ const scope = {
12137
+ state: "active",
12138
+ media: {},
12139
+ run: (callback) => runIn(active, callback),
12140
+ ignore: (callback) => runIn(undefined, callback),
12141
+ bind: active.bind,
12142
+ stop: () => kill(),
12143
+ cancel: () => kill(true),
12144
+ refresh() {
12145
+ clear(true);
12146
+ start();
12147
+ },
12148
+ };
12149
+ if (matchMedia && typeof window !== "undefined") {
12150
+ const conditions = typeof matchMedia === "string"
12151
+ ? { matches: matchMedia }
12152
+ : matchMedia;
12153
+ for (const name in conditions) {
12154
+ const query = window.matchMedia(toMediaQuery(conditions[name]));
12155
+ query.addEventListener("change", onChange);
12156
+ queries.push([name, query]);
12157
+ }
12158
+ }
12159
+ /**
12160
+ * A scope created inside another is cancelled with it.
12161
+ */
12162
+ addToScope(scope.cancel);
12163
+ start();
12164
+ return scope;
12165
+ }
12166
+
11936
12167
  function clearStatsBuffer() {
11937
12168
  statsBuffer.value = null;
11938
12169
  statsBuffer.addProjectionMetrics = null;
@@ -13549,7 +13780,9 @@
13549
13780
  }
13550
13781
  }
13551
13782
  function animateView(update, options = {}) {
13552
- return new ViewTransitionBuilder(update, options);
13783
+ const builder = new ViewTransitionBuilder(update, options);
13784
+ addBuilderToScope(builder);
13785
+ return builder;
13553
13786
  }
13554
13787
 
13555
13788
  /**
@@ -14058,7 +14291,12 @@
14058
14291
  */
14059
14292
  function animateLayout(scopeOrUpdateDom, updateDomOrOptions, options) {
14060
14293
  const { scope, updateDom, defaultOptions } = parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options);
14061
- return new LayoutAnimationBuilder(scope, updateDom, defaultOptions);
14294
+ const builder = new LayoutAnimationBuilder(scope, updateDom, {
14295
+ ...activeScope.current?.transition,
14296
+ ...defaultOptions,
14297
+ });
14298
+ addBuilderToScope(builder);
14299
+ return builder;
14062
14300
  }
14063
14301
 
14064
14302
  /**
@@ -14577,6 +14815,8 @@
14577
14815
  function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) {
14578
14816
  let animations = [];
14579
14817
  let animationOnComplete;
14818
+ const motionScope = activeScope.current;
14819
+ const scopeTransition = motionScope?.transition;
14580
14820
  const inherited = {};
14581
14821
  if (reduceMotion !== undefined)
14582
14822
  inherited.reduceMotion = reduceMotion;
@@ -14587,6 +14827,12 @@
14587
14827
  if (typeof onComplete === "function") {
14588
14828
  animationOnComplete = onComplete;
14589
14829
  }
14830
+ if (scopeTransition) {
14831
+ sequenceOptions.defaultTransition = {
14832
+ ...scopeTransition,
14833
+ ...sequenceOptions.defaultTransition,
14834
+ };
14835
+ }
14590
14836
  animations = animateSequence(subjectOrSequence, { ...inherited, ...sequenceOptions }, scope);
14591
14837
  }
14592
14838
  else {
@@ -14595,12 +14841,19 @@
14595
14841
  if (typeof onComplete === "function") {
14596
14842
  animationOnComplete = onComplete;
14597
14843
  }
14598
- animations = animateSubject(subjectOrSequence, optionsOrKeyframes, { ...inherited, ...rest }, scope);
14844
+ animations = animateSubject(subjectOrSequence, optionsOrKeyframes, {
14845
+ ...inherited,
14846
+ ...scopeTransition,
14847
+ ...rest,
14848
+ }, scope);
14599
14849
  }
14600
14850
  const animation = new GroupAnimationWithThen(animations);
14601
14851
  if (animationOnComplete) {
14602
14852
  animation.finished.then(animationOnComplete);
14603
14853
  }
14854
+ if (motionScope) {
14855
+ animation.finished.then(motionScope.add(() => animation.stop()));
14856
+ }
14604
14857
  if (scope) {
14605
14858
  scope.animations.push(animation);
14606
14859
  animation.finished.then(() => {
@@ -15328,9 +15581,11 @@
15328
15581
  container: source || container,
15329
15582
  ...options,
15330
15583
  };
15331
- return typeof onScroll === "function"
15332
- ? scrollInfo((info) => onScroll(info[axis].progress, info), optionsWithDefaults)
15333
- : attachToAnimation(onScroll, optionsWithDefaults);
15584
+ if (typeof onScroll === "function") {
15585
+ const callback = bindToScope(onScroll);
15586
+ return addToScope(scrollInfo((info) => callback(info[axis].progress, info), optionsWithDefaults));
15587
+ }
15588
+ return addToScope(attachToAnimation(onScroll, optionsWithDefaults));
15334
15589
  }
15335
15590
 
15336
15591
  const thresholds = {
@@ -15339,6 +15594,7 @@
15339
15594
  };
15340
15595
  function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "some" } = {}) {
15341
15596
  const elements = resolveElements(elementOrSelector);
15597
+ onStart = bindToScope(onStart);
15342
15598
  const activeIntersections = new WeakMap();
15343
15599
  const onIntersectionChange = (entries) => {
15344
15600
  entries.forEach((entry) => {
@@ -15370,7 +15626,7 @@
15370
15626
  threshold: typeof amount === "number" ? amount : thresholds[amount],
15371
15627
  });
15372
15628
  elements.forEach((element) => observer.observe(element));
15373
- return () => observer.disconnect();
15629
+ return addToScope(() => observer.disconnect());
15374
15630
  }
15375
15631
 
15376
15632
  exports.AsyncMotionValueAnimation = AsyncMotionValueAnimation;
@@ -15401,7 +15657,9 @@
15401
15657
  exports.VisualElement = VisualElement;
15402
15658
  exports.VisualElementDragControls = VisualElementDragControls;
15403
15659
  exports.acceleratedValues = acceleratedValues;
15660
+ exports.activeScope = activeScope;
15404
15661
  exports.addAttrValue = addAttrValue;
15662
+ exports.addBuilderToScope = addBuilderToScope;
15405
15663
  exports.addDomEvent = addDomEvent;
15406
15664
  exports.addEffect = addEffect;
15407
15665
  exports.addPointerEvent = addPointerEvent;
@@ -15409,6 +15667,7 @@
15409
15667
  exports.addSVGValue = addSVGValue;
15410
15668
  exports.addScaleCorrector = addScaleCorrector;
15411
15669
  exports.addStyleValue = addStyleValue;
15670
+ exports.addToScope = addToScope;
15412
15671
  exports.addUniqueItem = addUniqueItem;
15413
15672
  exports.addValueToWillChange = addValueToWillChange;
15414
15673
  exports.alpha = alpha;
@@ -15445,6 +15704,7 @@
15445
15704
  exports.backIn = backIn;
15446
15705
  exports.backInOut = backInOut;
15447
15706
  exports.backOut = backOut;
15707
+ exports.bindToScope = bindToScope;
15448
15708
  exports.boxEquals = boxEquals;
15449
15709
  exports.boxEqualsRounded = boxEqualsRounded;
15450
15710
  exports.buildHTMLStyles = buildHTMLStyles;
@@ -15639,6 +15899,7 @@
15639
15899
  exports.readStyleValue = readStyleValue;
15640
15900
  exports.readTransformValue = readTransformValue;
15641
15901
  exports.recordStats = recordStats;
15902
+ exports.releaseElement = releaseElement;
15642
15903
  exports.removeAxisDelta = removeAxisDelta;
15643
15904
  exports.removeAxisTransforms = removeAxisTransforms;
15644
15905
  exports.removeBoxTransforms = removeBoxTransforms;
@@ -15662,6 +15923,7 @@
15662
15923
  exports.scale = scale;
15663
15924
  exports.scaleCorrectors = scaleCorrectors;
15664
15925
  exports.scalePoint = scalePoint;
15926
+ exports.scopeMotion = scopeMotion;
15665
15927
  exports.scrapeHTMLMotionValuesFromProps = scrapeMotionValuesFromProps$1;
15666
15928
  exports.scrapeSVGMotionValuesFromProps = scrapeMotionValuesFromProps;
15667
15929
  exports.scroll = scroll;
@@ -15692,6 +15954,7 @@
15692
15954
  exports.sync = sync;
15693
15955
  exports.testValueType = testValueType;
15694
15956
  exports.time = time;
15957
+ exports.toMediaQuery = toMediaQuery;
15695
15958
  exports.transform = transform;
15696
15959
  exports.transformAxis = transformAxis;
15697
15960
  exports.transformBox = transformBox;