tiny-essentials 1.17.0 → 1.17.1

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.
@@ -3699,6 +3699,19 @@ const {
3699
3699
  areElsCollRight: TinyHtml_areElsCollRight,
3700
3700
  } = collision_namespaceObject;
3701
3701
 
3702
+ /**
3703
+ * Callback invoked on each animation frame with the current scroll position,
3704
+ * normalized animation time (`0` to `1`), and a completion flag.
3705
+ *
3706
+ * @typedef {(progress: { x: number, y: number, isComplete: boolean, time: number }) => void} OnScrollAnimation
3707
+ */
3708
+
3709
+ /**
3710
+ * A list of supported easing function names for smooth animations.
3711
+ *
3712
+ * @typedef {'linear' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'} Easings
3713
+ */
3714
+
3702
3715
  /**
3703
3716
  * Represents a raw Node element or an instance of TinyHtml.
3704
3717
  * This type is used to abstract interactions with both plain elements
@@ -6107,7 +6120,7 @@ class TinyHtml_TinyHtml {
6107
6120
  */
6108
6121
  static setWinScrollTop(value) {
6109
6122
  if (typeof value !== 'number') throw new TypeError('The value must be a number.');
6110
- window.scrollTo({ top: value });
6123
+ TinyHtml_TinyHtml.setScrollTop(window, value);
6111
6124
  }
6112
6125
 
6113
6126
  /**
@@ -6116,7 +6129,7 @@ class TinyHtml_TinyHtml {
6116
6129
  */
6117
6130
  static setWinScrollLeft(value) {
6118
6131
  if (typeof value !== 'number') throw new TypeError('The value must be a number.');
6119
- window.scrollTo({ left: value });
6132
+ TinyHtml_TinyHtml.setScrollLeft(window, value);
6120
6133
  }
6121
6134
 
6122
6135
  /**
@@ -6433,6 +6446,30 @@ class TinyHtml_TinyHtml {
6433
6446
 
6434
6447
  //////////////////////////////////////////////////
6435
6448
 
6449
+ /**
6450
+ * Applies an animation to one or multiple TinyElement instances.
6451
+ *
6452
+ * @param {TinyElement|TinyElement[]} el - A single TinyElement or an array of TinyElements to animate.
6453
+ * @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
6454
+ * @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
6455
+ * @returns {TinyElement|TinyElement[]}
6456
+ */
6457
+ static animate(el, keyframes, ops) {
6458
+ TinyHtml_TinyHtml._preElems(el, 'animate').forEach((elem) => elem.animate(keyframes, ops));
6459
+ return el;
6460
+ }
6461
+
6462
+ /**
6463
+ * Applies an animation to one or multiple TinyElement instances.
6464
+ *
6465
+ * @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
6466
+ * @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
6467
+ * @returns {TinyElement|TinyElement[]}
6468
+ */
6469
+ animate(keyframes, ops) {
6470
+ return TinyHtml_TinyHtml.animate(this, keyframes, ops);
6471
+ }
6472
+
6436
6473
  /**
6437
6474
  * Gets the offset of the element relative to the document.
6438
6475
  * @param {TinyElement} el - Target element.
@@ -6588,26 +6625,147 @@ class TinyHtml_TinyHtml {
6588
6625
  }
6589
6626
 
6590
6627
  /**
6591
- * Sets the vertical scroll position.
6592
- * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
6593
- * @param {number} value - Scroll top value.
6594
- * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6628
+ * Collection of easing functions used for scroll and animation calculations.
6629
+ * Each function receives a normalized time value (`t` from 0 to 1) and returns the eased progress.
6630
+ *
6631
+ * @type {Record<string, (t: number) => number>}
6595
6632
  */
6596
- static setScrollTop(el, value) {
6597
- if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
6598
- TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollTop').forEach((elem) => {
6599
- if (TinyHtml_TinyHtml.isWindow(elem)) {
6600
- elem.scrollTo(elem.pageXOffset, value);
6633
+ static easings = {
6634
+ linear: (t) => t,
6635
+ easeInQuad: (t) => t * t,
6636
+ easeOutQuad: (t) => t * (2 - t),
6637
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
6638
+ easeInCubic: (t) => t * t * t,
6639
+ easeOutCubic: (t) => --t * t * t + 1,
6640
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
6641
+ };
6642
+
6643
+ /**
6644
+ * Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
6645
+ * using a custom duration and easing function.
6646
+ *
6647
+ * If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
6648
+ *
6649
+ * @param {TinyElementAndWindow | TinyElementAndWindow[]} el - A single element, array of elements, or the window to scroll.
6650
+ * @param {Object} [settings={}] - Configuration object for the scroll animation.
6651
+ * @param {number} [settings.targetX] - The horizontal scroll target in pixels.
6652
+ * @param {number} [settings.targetY] - The vertical scroll target in pixels.
6653
+ * @param {number} [settings.duration] - The duration of the animation in milliseconds.
6654
+ * @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
6655
+ * @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
6656
+ * frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
6657
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6658
+ * @throws {TypeError} If `el` is not a valid element, array, or window.
6659
+ * @throws {TypeError} If `targetX` or `targetY` is defined but not a number.
6660
+ * @throws {TypeError} If `duration` is defined but not a number.
6661
+ * @throws {TypeError} If `easing` is defined but not a valid easing function name.
6662
+ * @throws {TypeError} If `onAnimation` is defined but not a function.
6663
+ */
6664
+ static scrollToXY(el, { targetX, targetY, duration, easing, onAnimation } = {}) {
6665
+ if (targetX !== undefined && typeof targetX !== 'number')
6666
+ throw new TypeError('`targetX` must be a number if provided.');
6667
+ if (targetY !== undefined && typeof targetY !== 'number')
6668
+ throw new TypeError('`targetY` must be a number if provided.');
6669
+ if (duration !== undefined && typeof duration !== 'number')
6670
+ throw new TypeError('`duration` must be a number if provided.');
6671
+ if (easing !== undefined && typeof easing !== 'string')
6672
+ throw new TypeError('`easing` must be a string if provided.');
6673
+ if (easing !== undefined && typeof TinyHtml_TinyHtml.easings[easing] !== 'function')
6674
+ throw new TypeError(`Unknown easing function: "${easing}".`);
6675
+ if (onAnimation !== undefined && typeof onAnimation !== 'function')
6676
+ throw new TypeError('`onAnimation` must be a function if provided.');
6677
+
6678
+ /**
6679
+ * Performs an instant scroll to the given coordinates.
6680
+ *
6681
+ * @param {ElementAndWindow} elem - The element or window to scroll.
6682
+ * @param {number} newX - The final horizontal scroll position.
6683
+ * @param {number} newY - The final vertical scroll position.
6684
+ * @param {number} time - Normalized progress value.
6685
+ */
6686
+ const executeScroll = (elem, newX, newY, time) => {
6687
+ if (elem instanceof Window) {
6688
+ window.scrollTo(newX, newY);
6601
6689
  } else if (elem.nodeType === 9) {
6602
6690
  // @ts-ignore
6603
- elem.defaultView.scrollTo(elem.defaultView.pageXOffset, value);
6691
+ elem.defaultView.scrollTo(newX, newY);
6604
6692
  } else {
6605
- elem.scrollTop = value;
6693
+ const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
6694
+ const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
6695
+ if (startX !== newX) elem.scrollLeft = newX;
6696
+ if (startY !== newY) elem.scrollTop = newY;
6697
+ }
6698
+ if (typeof onAnimation === 'function')
6699
+ onAnimation({ x: newX, y: newY, isComplete: time >= 1, time });
6700
+ };
6701
+
6702
+ TinyHtml_TinyHtml._preElemsAndWindow(el, 'scrollToXY').forEach((elem) => {
6703
+ const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
6704
+ const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
6705
+ const targX = targetX ?? startX;
6706
+ const targY = targetY ?? startY;
6707
+
6708
+ const changeX = targX - startX;
6709
+ const changeY = targY - startY;
6710
+
6711
+ const ease = (typeof easing === 'string' && TinyHtml_TinyHtml.easings[easing]) || null;
6712
+ if (typeof duration !== 'number' || typeof ease !== 'function')
6713
+ return executeScroll(elem, targX, targY, 1);
6714
+ const startTime = performance.now();
6715
+ const dur = duration ?? 0;
6716
+
6717
+ /**
6718
+ * Animates the scroll position based on easing and time.
6719
+ *
6720
+ * @param {number} currentTime - Timestamp provided by requestAnimationFrame.
6721
+ */
6722
+ function animateScroll(currentTime) {
6723
+ if (typeof ease !== 'function') return;
6724
+ const time = Math.min(1, (currentTime - startTime) / dur);
6725
+ const easedTime = ease(time);
6726
+
6727
+ const newX = startX + changeX * easedTime;
6728
+ const newY = startY + changeY * easedTime;
6729
+ executeScroll(elem, newX, newY, time);
6730
+
6731
+ if (time < 1) requestAnimationFrame(animateScroll);
6606
6732
  }
6733
+
6734
+ requestAnimationFrame(animateScroll);
6607
6735
  });
6608
6736
  return el;
6609
6737
  }
6610
6738
 
6739
+ /**
6740
+ * Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
6741
+ * using a custom duration and easing function.
6742
+ *
6743
+ * If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
6744
+ *
6745
+ * @param {Object} [settings={}] - Configuration object for the scroll animation.
6746
+ * @param {number} [settings.targetX] - The horizontal scroll target in pixels.
6747
+ * @param {number} [settings.targetY] - The vertical scroll target in pixels.
6748
+ * @param {number} [settings.duration] - The duration of the animation in milliseconds.
6749
+ * @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
6750
+ * @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
6751
+ * frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
6752
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6753
+ */
6754
+ scrollToXY({ targetX, targetY, duration, easing, onAnimation } = {}) {
6755
+ return TinyHtml_TinyHtml.scrollToXY(this, { targetX, targetY, duration, easing, onAnimation });
6756
+ }
6757
+
6758
+ /**
6759
+ * Sets the vertical scroll position.
6760
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
6761
+ * @param {number} value - Scroll top value.
6762
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6763
+ */
6764
+ static setScrollTop(el, value) {
6765
+ if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
6766
+ return TinyHtml_TinyHtml.scrollToXY(el, { targetY: value });
6767
+ }
6768
+
6611
6769
  /**
6612
6770
  * Sets the vertical scroll position.
6613
6771
  * @param {number} value - Scroll top value.
@@ -6625,17 +6783,7 @@ class TinyHtml_TinyHtml {
6625
6783
  */
6626
6784
  static setScrollLeft(el, value) {
6627
6785
  if (typeof value !== 'number') throw new TypeError('ScrollLeft value must be a number.');
6628
- TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollLeft').forEach((elem) => {
6629
- if (TinyHtml_TinyHtml.isWindow(elem)) {
6630
- elem.scrollTo(value, elem.pageYOffset);
6631
- } else if (elem.nodeType === 9) {
6632
- // @ts-ignore
6633
- elem.defaultView.scrollTo(value, elem.defaultView.pageYOffset);
6634
- } else {
6635
- elem.scrollLeft = value;
6636
- }
6637
- });
6638
- return el;
6786
+ return TinyHtml_TinyHtml.scrollToXY(el, { targetX: value });
6639
6787
  }
6640
6788
 
6641
6789
  /**