tiny-essentials 1.14.0 → 1.15.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.
@@ -2143,6 +2143,16 @@ __webpack_require__.r(__webpack_exports__);
2143
2143
  // EXPORTS
2144
2144
  __webpack_require__.d(__webpack_exports__, {
2145
2145
  addAiMarkerShortcut: () => (/* reexport */ addAiMarkerShortcut),
2146
+ areElsCollBottom: () => (/* reexport */ collision_areElsCollBottom),
2147
+ areElsCollLeft: () => (/* reexport */ collision_areElsCollLeft),
2148
+ areElsCollPerfBottom: () => (/* reexport */ areElsCollPerfBottom),
2149
+ areElsCollPerfLeft: () => (/* reexport */ areElsCollPerfLeft),
2150
+ areElsCollPerfRight: () => (/* reexport */ areElsCollPerfRight),
2151
+ areElsCollPerfTop: () => (/* reexport */ areElsCollPerfTop),
2152
+ areElsCollRight: () => (/* reexport */ collision_areElsCollRight),
2153
+ areElsCollTop: () => (/* reexport */ collision_areElsCollTop),
2154
+ areElsColliding: () => (/* reexport */ collision_areElsColliding),
2155
+ areElsPerfColliding: () => (/* reexport */ areElsPerfColliding),
2146
2156
  areHtmlElsColliding: () => (/* reexport */ areHtmlElsColliding),
2147
2157
  arraySortPositions: () => (/* reexport */ arraySortPositions),
2148
2158
  asyncReplace: () => (/* reexport */ asyncReplace),
@@ -2158,16 +2168,26 @@ __webpack_require__.d(__webpack_exports__, {
2158
2168
  formatTimer: () => (/* reexport */ formatTimer),
2159
2169
  genFibonacciSeq: () => (/* reexport */ genFibonacciSeq),
2160
2170
  getAge: () => (/* reexport */ getAge),
2171
+ getElsCollDetails: () => (/* reexport */ getElsCollDetails),
2172
+ getElsCollDirDepth: () => (/* reexport */ getElsCollDirDepth),
2173
+ getElsCollOverlap: () => (/* reexport */ getElsCollOverlap),
2174
+ getElsCollOverlapPos: () => (/* reexport */ getElsCollOverlapPos),
2175
+ getElsColliding: () => (/* reexport */ getElsColliding),
2176
+ getElsPerfColliding: () => (/* reexport */ getElsPerfColliding),
2177
+ getElsRelativeCenterOffset: () => (/* reexport */ getElsRelativeCenterOffset),
2161
2178
  getHtmlElBorders: () => (/* reexport */ getHtmlElBorders),
2162
2179
  getHtmlElBordersWidth: () => (/* reexport */ getHtmlElBordersWidth),
2163
2180
  getHtmlElMargin: () => (/* reexport */ getHtmlElMargin),
2164
2181
  getHtmlElPadding: () => (/* reexport */ getHtmlElPadding),
2182
+ getRectCenter: () => (/* reexport */ getRectCenter),
2165
2183
  getSimplePerc: () => (/* reexport */ getSimplePerc),
2166
2184
  getTimeDuration: () => (/* reexport */ getTimeDuration),
2167
2185
  installWindowHiddenScript: () => (/* reexport */ installWindowHiddenScript),
2168
2186
  isFullScreenMode: () => (/* reexport */ isFullScreenMode),
2187
+ isInViewport: () => (/* reexport */ isInViewport),
2169
2188
  isJsonObject: () => (/* reexport */ isJsonObject),
2170
2189
  isScreenFilled: () => (/* reexport */ isScreenFilled),
2190
+ isScrolledIntoView: () => (/* reexport */ isScrolledIntoView),
2171
2191
  objType: () => (/* reexport */ objType),
2172
2192
  offFullScreenChange: () => (/* reexport */ offFullScreenChange),
2173
2193
  onFullScreenChange: () => (/* reexport */ onFullScreenChange),
@@ -2851,9 +2871,404 @@ extendObjType([
2851
2871
  ],
2852
2872
  ]);
2853
2873
 
2874
+ ;// ./src/v1/basics/collision.mjs
2875
+ /**
2876
+ * A direction relative to a rectangle.
2877
+ *
2878
+ * Represents one of the four cardinal directions from the perspective of the element.
2879
+ *
2880
+ * @typedef {'top' | 'bottom' | 'left' | 'right'} Dirs
2881
+ */
2882
+
2883
+ /**
2884
+ * Represents all directional aspects of a collision.
2885
+ *
2886
+ * @typedef {Object} CollDirs
2887
+ * @property {Dirs | 'center' | null} in - The dominant direction of entry. `'center'` if all sides are equally overlapped. `null` if no collision.
2888
+ * @property {Dirs | null} x - The horizontal direction (`'left'` or `'right'`) the collision is biased toward, or `null`.
2889
+ * @property {Dirs | null} y - The vertical direction (`'top'` or `'bottom'`) the collision is biased toward, or `null`.
2890
+ */
2891
+
2892
+ /**
2893
+ * Indicates if a collision is in the negative direction (rect2 is outside rect1).
2894
+ *
2895
+ * @typedef {Object} NegCollDirs
2896
+ * @property {Dirs | null} x - Horizontal direction of negative overlap, if any.
2897
+ * @property {Dirs | null} y - Vertical direction of negative overlap, if any.
2898
+ */
2899
+
2900
+ /**
2901
+ * Collision depth values from each side of rect2 inside rect1.
2902
+ *
2903
+ * Positive values indicate penetration; negative values indicate gaps.
2904
+ *
2905
+ * @typedef {Object} CollData
2906
+ * @property {number} top - Depth from rect2's top into rect1.
2907
+ * @property {number} bottom - Depth from rect2's bottom into rect1.
2908
+ * @property {number} left - Depth from rect2's left into rect1.
2909
+ * @property {number} right - Depth from rect2's right into rect1.
2910
+ */
2911
+
2912
+ /**
2913
+ * X and Y offset representing center difference between two rectangles.
2914
+ *
2915
+ * Useful to measure how far one element's center is from another.
2916
+ *
2917
+ * @typedef {Object} CollCenter
2918
+ * @property {number} x - Horizontal distance in pixels from rect1's center to rect2's center.
2919
+ * @property {number} y - Vertical distance in pixels from rect1's center to rect2's center.
2920
+ */
2921
+
2922
+ /**
2923
+ * Represents a rectangular area in absolute pixel values.
2924
+ *
2925
+ * Similar to `DOMRect`, this object describes the dimensions and position of a box
2926
+ * in the 2D plane, typically representing an element's bounding box.
2927
+ *
2928
+ * @typedef {Object} ObjRect
2929
+ * @property {number} height - The total height of the rectangle in pixels.
2930
+ * @property {number} width - The total width of the rectangle in pixels.
2931
+ * @property {number} top - The Y-coordinate of the top edge of the rectangle.
2932
+ * @property {number} bottom - The Y-coordinate of the bottom edge of the rectangle.
2933
+ * @property {number} left - The X-coordinate of the left edge of the rectangle.
2934
+ * @property {number} right - The X-coordinate of the right edge of the rectangle.
2935
+ */
2936
+
2937
+ // Normal collision checks (loose overlap detection)
2938
+
2939
+ /**
2940
+ * Checks if rect1 is completely above rect2 (no vertical overlap).
2941
+ *
2942
+ * @param {ObjRect} rect1 - The bounding rectangle of the first element.
2943
+ * @param {ObjRect} rect2 - The bounding rectangle of the second element.
2944
+ * @returns {boolean} True if rect1 is entirely above rect2.
2945
+ */
2946
+ const collision_areElsCollTop = (rect1, rect2) => rect1.bottom < rect2.top;
2947
+
2948
+ /**
2949
+ * Checks if rect1 is completely below rect2 (no vertical overlap).
2950
+ *
2951
+ * @param {ObjRect} rect1
2952
+ * @param {ObjRect} rect2
2953
+ * @returns {boolean} True if rect1 is entirely below rect2.
2954
+ */
2955
+ const collision_areElsCollBottom = (rect1, rect2) => rect1.top > rect2.bottom;
2956
+
2957
+ /**
2958
+ * Checks if rect1 is completely to the left of rect2 (no horizontal overlap).
2959
+ *
2960
+ * @param {ObjRect} rect1
2961
+ * @param {ObjRect} rect2
2962
+ * @returns {boolean} True if rect1 is entirely to the left of rect2.
2963
+ */
2964
+ const collision_areElsCollLeft = (rect1, rect2) => rect1.right < rect2.left;
2965
+
2966
+ /**
2967
+ * Checks if rect1 is completely to the right of rect2 (no horizontal overlap).
2968
+ *
2969
+ * @param {ObjRect} rect1
2970
+ * @param {ObjRect} rect2
2971
+ * @returns {boolean} True if rect1 is entirely to the right of rect2.
2972
+ */
2973
+ const collision_areElsCollRight = (rect1, rect2) => rect1.left > rect2.right;
2974
+
2975
+ // Perfect collision checks (touch included)
2976
+
2977
+ /**
2978
+ * Checks if rect1 is perfectly above rect2 (no vertical touch or overlap).
2979
+ *
2980
+ * @param {ObjRect} rect1
2981
+ * @param {ObjRect} rect2
2982
+ * @returns {boolean} True if rect1 is fully above or touching rect2's top.
2983
+ */
2984
+ const areElsCollPerfTop = (rect1, rect2) => rect1.bottom <= rect2.top;
2985
+
2986
+ /**
2987
+ * Checks if rect1 is perfectly below rect2 (no vertical touch or overlap).
2988
+ *
2989
+ * @param {ObjRect} rect1
2990
+ * @param {ObjRect} rect2
2991
+ * @returns {boolean} True if rect1 is fully below or touching rect2's bottom.
2992
+ */
2993
+ const areElsCollPerfBottom = (rect1, rect2) => rect1.top >= rect2.bottom;
2994
+
2995
+ /**
2996
+ * Checks if rect1 is perfectly to the left of rect2 (no horizontal touch or overlap).
2997
+ *
2998
+ * @param {ObjRect} rect1
2999
+ * @param {ObjRect} rect2
3000
+ * @returns {boolean} True if rect1 is fully left or touching rect2's left.
3001
+ */
3002
+ const areElsCollPerfLeft = (rect1, rect2) => rect1.right <= rect2.left;
3003
+
3004
+ /**
3005
+ * Checks if rect1 is perfectly to the right of rect2 (no horizontal touch or overlap).
3006
+ *
3007
+ * @param {ObjRect} rect1
3008
+ * @param {ObjRect} rect2
3009
+ * @returns {boolean} True if rect1 is fully right or touching rect2's right.
3010
+ */
3011
+ const areElsCollPerfRight = (rect1, rect2) => rect1.left >= rect2.right;
3012
+
3013
+ // Main collision check
3014
+
3015
+ /**
3016
+ * Returns true if rect1 and rect2 are colliding (partially or fully overlapping).
3017
+ *
3018
+ * @param {ObjRect} rect1
3019
+ * @param {ObjRect} rect2
3020
+ * @returns {boolean} True if there's any collision between rect1 and rect2.
3021
+ */
3022
+ const collision_areElsColliding = (rect1, rect2) =>
3023
+ !(
3024
+ collision_areElsCollLeft(rect1, rect2) ||
3025
+ collision_areElsCollRight(rect1, rect2) ||
3026
+ collision_areElsCollTop(rect1, rect2) ||
3027
+ collision_areElsCollBottom(rect1, rect2)
3028
+ );
3029
+
3030
+ /**
3031
+ * Returns true if rect1 and rect2 are colliding or perfectly touching.
3032
+ *
3033
+ * @param {ObjRect} rect1
3034
+ * @param {ObjRect} rect2
3035
+ * @returns {boolean} True if there's any contact or overlap.
3036
+ */
3037
+ const areElsPerfColliding = (rect1, rect2) =>
3038
+ !(
3039
+ areElsCollPerfLeft(rect1, rect2) ||
3040
+ areElsCollPerfRight(rect1, rect2) ||
3041
+ areElsCollPerfTop(rect1, rect2) ||
3042
+ areElsCollPerfBottom(rect1, rect2)
3043
+ );
3044
+
3045
+ // Collision direction guess (loose and perfect)
3046
+
3047
+ /**
3048
+ * Attempts to determine the direction rect1 entered rect2 based on loose overlap rules.
3049
+ *
3050
+ * @param {ObjRect} rect1
3051
+ * @param {ObjRect} rect2
3052
+ * @returns {string|null} 'top' | 'bottom' | 'left' | 'right' | null
3053
+ */
3054
+ const getElsColliding = (rect1, rect2) => {
3055
+ if (collision_areElsCollLeft(rect1, rect2)) return 'left';
3056
+ else if (collision_areElsCollRight(rect1, rect2)) return 'right';
3057
+ else if (collision_areElsCollTop(rect1, rect2)) return 'top';
3058
+ else if (collision_areElsCollBottom(rect1, rect2)) return 'bottom';
3059
+ return null;
3060
+ };
3061
+
3062
+ /**
3063
+ * Attempts to determine the direction rect1 touched or entered rect2 using perfect mode.
3064
+ *
3065
+ * @param {ObjRect} rect1
3066
+ * @param {ObjRect} rect2
3067
+ * @returns {'top' | 'bottom' | 'left' | 'right' | null}
3068
+ */
3069
+ const getElsPerfColliding = (rect1, rect2) => {
3070
+ if (areElsCollPerfLeft(rect1, rect2)) return 'left';
3071
+ else if (areElsCollPerfRight(rect1, rect2)) return 'right';
3072
+ else if (areElsCollPerfTop(rect1, rect2)) return 'top';
3073
+ else if (areElsCollPerfBottom(rect1, rect2)) return 'bottom';
3074
+ return null;
3075
+ };
3076
+
3077
+ // Overlap Calculation
3078
+
3079
+ /**
3080
+ * Calculates overlap values between rect1 and rect2 in all directions.
3081
+ *
3082
+ * @param {ObjRect} rect1
3083
+ * @param {ObjRect} rect2
3084
+ * @returns {{
3085
+ * overlapLeft: number,
3086
+ * overlapRight: number,
3087
+ * overlapTop: number,
3088
+ * overlapBottom: number
3089
+ * }} Distance of overlap from each direction (can be negative).
3090
+ */
3091
+ const getElsCollOverlap = (rect1, rect2) => ({
3092
+ overlapLeft: rect2.right - rect1.left,
3093
+ overlapRight: rect1.right - rect2.left,
3094
+ overlapTop: rect2.bottom - rect1.top,
3095
+ overlapBottom: rect1.bottom - rect2.top,
3096
+ });
3097
+
3098
+ /**
3099
+ * Determines directional collision based on overlap depth.
3100
+ *
3101
+ * @param {Object} [settings={}]
3102
+ * @param {number} [settings.overlapLeft]
3103
+ * @param {number} [settings.overlapRight]
3104
+ * @param {number} [settings.overlapTop]
3105
+ * @param {number} [settings.overlapBottom]
3106
+ * @returns {{ dirX: Dirs, dirY: Dirs }} Direction of strongest X/Y overlap.
3107
+ */
3108
+ const getElsCollOverlapPos = ({
3109
+ overlapLeft = -1,
3110
+ overlapRight = -1,
3111
+ overlapTop = -1,
3112
+ overlapBottom = -1,
3113
+ } = {}) => ({
3114
+ dirX: overlapLeft < overlapRight ? 'right' : 'left',
3115
+ dirY: overlapTop < overlapBottom ? 'bottom' : 'top',
3116
+ });
3117
+
3118
+ // Center utils
3119
+
3120
+ /**
3121
+ * Calculates the center point (X and Y) of a given Rect.
3122
+ *
3123
+ * @param {ObjRect} rect - The bounding rectangle of the element.
3124
+ * @returns {{ x: number, y: number }} An object with the `x` and `y` coordinates of the center.
3125
+ */
3126
+ const getRectCenter = (rect) => ({
3127
+ x: rect.left + rect.width / 2,
3128
+ y: rect.top + rect.height / 2,
3129
+ });
3130
+
3131
+ /**
3132
+ * Calculates the offset between the center of rect2 and the center of rect1.
3133
+ *
3134
+ * The values will be 0 when rect1 is perfectly centered over rect2.
3135
+ *
3136
+ * @param {ObjRect} rect1 - The bounding rectangle of the reference element.
3137
+ * @param {ObjRect} rect2 - The bounding rectangle of the element being compared.
3138
+ * @returns {{
3139
+ * x: number,
3140
+ * y: number
3141
+ * }} An object with the X and Y offset in pixels from rect1's center to rect2's center.
3142
+ */
3143
+ function getElsRelativeCenterOffset(rect1, rect2) {
3144
+ const center1X = rect1.left + rect1.width / 2;
3145
+ const center1Y = rect1.top + rect1.height / 2;
3146
+
3147
+ const center2X = rect2.left + rect2.width / 2;
3148
+ const center2Y = rect2.top + rect2.height / 2;
3149
+
3150
+ return {
3151
+ x: center2X - center1X,
3152
+ y: center2Y - center1Y,
3153
+ };
3154
+ }
3155
+
3156
+ // Direction & Depth detection
3157
+
3158
+ /**
3159
+ * Detects the direction of the dominant collision between two elements
3160
+ * and calculates how deep the overlap is in both x and y axes.
3161
+ *
3162
+ * @param {ObjRect} rect1 - The bounding rectangle of the first element.
3163
+ * @param {ObjRect} rect2 - The bounding rectangle of the second element.
3164
+ * @returns {{
3165
+ * inDir: Dirs | null;
3166
+ * dirX: Dirs | null;
3167
+ * dirY: Dirs | null;
3168
+ * depthX: number;
3169
+ * depthY: number;
3170
+ * }} An object containing the collision direction and how deep the overlap is.
3171
+ */
3172
+ function getElsCollDirDepth(rect1, rect2) {
3173
+ if (!areElsPerfColliding(rect1, rect2))
3174
+ return {
3175
+ inDir: null,
3176
+ dirX: null,
3177
+ dirY: null,
3178
+ depthX: 0,
3179
+ depthY: 0,
3180
+ };
3181
+
3182
+ const { overlapLeft, overlapRight, overlapTop, overlapBottom } = getElsCollOverlap(rect1, rect2);
3183
+ const { dirX, dirY } = getElsCollOverlapPos({
3184
+ overlapLeft,
3185
+ overlapRight,
3186
+ overlapTop,
3187
+ overlapBottom,
3188
+ });
3189
+ const depthX = Math.min(overlapLeft, overlapRight);
3190
+ const depthY = Math.min(overlapTop, overlapBottom);
3191
+
3192
+ /** @type {Dirs} */
3193
+ let inDir;
3194
+
3195
+ if (depthX < depthY) inDir = dirX;
3196
+ else inDir = dirY;
3197
+ return { inDir, dirX, dirY, depthX, depthY };
3198
+ }
3199
+
3200
+ // Full detail report
3201
+
3202
+ /**
3203
+ * Detects the collision direction and depth between two DOMRects.
3204
+ *
3205
+ * @param {ObjRect} rect1 - The bounding rectangle of the first element.
3206
+ * @param {ObjRect} rect2 - The bounding rectangle of the second element.
3207
+ * @returns {{ depth: CollData; dirs: CollDirs; isNeg: NegCollDirs; }} Collision info or null if no collision is detected.
3208
+ */
3209
+ function getElsCollDetails(rect1, rect2) {
3210
+ const isColliding = areElsPerfColliding(rect1, rect2);
3211
+
3212
+ /** @type {CollDirs} */
3213
+ const dirs = { in: null, x: null, y: null };
3214
+
3215
+ /** @type {NegCollDirs} */
3216
+ const isNeg = { y: null, x: null };
3217
+
3218
+ /** @type {Record<Dirs, number>} */
3219
+ const depth = { top: 0, bottom: 0, left: 0, right: 0 };
3220
+
3221
+ // Depth
3222
+ // Yes, it's actually reversed the values orders
3223
+ const { overlapLeft, overlapRight, overlapTop, overlapBottom } = getElsCollOverlap(rect2, rect1);
3224
+ depth.top = overlapTop;
3225
+ depth.bottom = overlapBottom;
3226
+ depth.left = overlapLeft;
3227
+ depth.right = overlapRight;
3228
+
3229
+ // Dirs
3230
+ /**
3231
+ * Detect the direction with the smallest positive overlap (entry point)
3232
+ * @type {[Dirs, number][]}
3233
+ */
3234
+ // @ts-ignore
3235
+ const entries = Object.entries(depth)
3236
+ .filter(([, val]) => val > 0)
3237
+ .sort((a, b) => a[1] - b[1]);
3238
+
3239
+ // Yes, it's actually reversed the values orders here too
3240
+ const { dirX, dirY } = getElsCollOverlapPos({
3241
+ overlapLeft: overlapRight,
3242
+ overlapRight: overlapLeft,
3243
+ overlapTop: overlapBottom,
3244
+ overlapBottom: overlapTop,
3245
+ });
3246
+ dirs.y = dirY;
3247
+ dirs.x = dirX;
3248
+
3249
+ // isNeg
3250
+ if (depth.bottom < 0) isNeg.y = 'bottom';
3251
+ else if (depth.top < 0) isNeg.y = 'top';
3252
+ if (depth.left < 0) isNeg.x = 'left';
3253
+ else if (depth.right < 0) isNeg.x = 'right';
3254
+
3255
+ // Inside Dir
3256
+ dirs.in = isColliding
3257
+ ? depth.top === depth.bottom && depth.bottom === depth.left && depth.left === depth.right
3258
+ ? 'center'
3259
+ : entries.length
3260
+ ? entries[0][0]
3261
+ : 'top'
3262
+ : null; // fallback in case of exact match
3263
+
3264
+ // Complete
3265
+ return { dirs, depth, isNeg };
3266
+ }
3267
+
2854
3268
  ;// ./src/v1/basics/html.mjs
2855
3269
 
2856
3270
 
3271
+
2857
3272
  /**
2858
3273
  * Checks if two DOM elements are colliding on the screen.
2859
3274
  *
@@ -2864,13 +3279,55 @@ extendObjType([
2864
3279
  function areHtmlElsColliding(elem1, elem2) {
2865
3280
  const rect1 = elem1.getBoundingClientRect();
2866
3281
  const rect2 = elem2.getBoundingClientRect();
3282
+ return collision_areElsColliding(rect1, rect2);
3283
+ }
2867
3284
 
2868
- return !(
2869
- rect1.right < rect2.left ||
2870
- rect1.left > rect2.right ||
2871
- rect1.bottom < rect2.top ||
2872
- rect1.top > rect2.bottom
2873
- );
3285
+ /**
3286
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
3287
+ * until the element exits through the same side it entered.
3288
+ *
3289
+ * @param {Element} elem1 - First DOM element (e.g. draggable or moving element).
3290
+ * @param {Element} elem2 - Second DOM element (e.g. a container or boundary element).
3291
+ * @param {'top'|'bottom'|'left'|'right'} lockDirection - Direction that must be respected to unlock the collision.
3292
+ * @param {WeakMap<Element, string>} stateMap - A shared WeakMap to track persistent entry direction per element.
3293
+ * @returns {boolean} True if collision is still active.
3294
+ */
3295
+ function areHtmlElsCollidingWithLock(elem1, elem2, lockDirection, stateMap) {
3296
+ const rect1 = elem1.getBoundingClientRect();
3297
+ const rect2 = elem2.getBoundingClientRect();
3298
+ const isColliding = areElsColliding(rect1, rect2);
3299
+
3300
+ if (isColliding) {
3301
+ // Save entry direction
3302
+ if (!stateMap.has(elem1)) {
3303
+ stateMap.set(elem1, lockDirection);
3304
+ }
3305
+ return true;
3306
+ }
3307
+
3308
+ // Handle unlock logic
3309
+ if (stateMap.has(elem1)) {
3310
+ const lastDirection = stateMap.get(elem1);
3311
+
3312
+ switch (lastDirection) {
3313
+ case 'top':
3314
+ if (areElsCollTop(rect1, rect2)) stateMap.delete(elem1); // exited from top
3315
+ break;
3316
+ case 'bottom':
3317
+ if (areElsCollBottom(rect1, rect2)) stateMap.delete(elem1); // exited from bottom
3318
+ break;
3319
+ case 'left':
3320
+ if (areElsCollLeft(rect1, rect2)) stateMap.delete(elem1); // exited from left
3321
+ break;
3322
+ case 'right':
3323
+ if (areElsCollRight(rect1, rect2)) stateMap.delete(elem1); // exited from right
3324
+ break;
3325
+ }
3326
+
3327
+ return stateMap.has(elem1); // still colliding (locked)
3328
+ }
3329
+
3330
+ return false;
2874
3331
  }
2875
3332
 
2876
3333
  /**
@@ -3299,6 +3756,38 @@ function installWindowHiddenScript({
3299
3756
  return uninstall;
3300
3757
  }
3301
3758
 
3759
+ /**
3760
+ * Checks if the given element is at least partially visible in the viewport.
3761
+ *
3762
+ * @param {HTMLElement} element - The DOM element to check.
3763
+ * @returns {boolean} True if the element is partially in the viewport, false otherwise.
3764
+ */
3765
+ function isInViewport(element) {
3766
+ const elementTop = element.offsetTop;
3767
+ const elementBottom = elementTop + element.offsetHeight;
3768
+
3769
+ const viewportTop = window.scrollY;
3770
+ const viewportBottom = viewportTop + window.innerHeight;
3771
+
3772
+ return elementBottom > viewportTop && elementTop < viewportBottom;
3773
+ }
3774
+
3775
+ /**
3776
+ * Checks if the given element is fully visible in the viewport (top and bottom).
3777
+ *
3778
+ * @param {HTMLElement} element - The DOM element to check.
3779
+ * @returns {boolean} True if the element is fully visible in the viewport, false otherwise.
3780
+ */
3781
+ function isScrolledIntoView(element) {
3782
+ const viewportTop = window.scrollY;
3783
+ const viewportBottom = viewportTop + window.innerHeight;
3784
+
3785
+ const elemTop = element.offsetTop;
3786
+ const elemBottom = elemTop + element.offsetHeight;
3787
+
3788
+ return elemBottom <= viewportBottom && elemTop >= viewportTop;
3789
+ }
3790
+
3302
3791
  ;// ./src/v1/basics/fullScreen.mjs
3303
3792
  /**
3304
3793
  * Checks if the document is currently in fullscreen mode.
@@ -3733,6 +4222,7 @@ export default KeyPressHandler;
3733
4222
 
3734
4223
 
3735
4224
 
4225
+
3736
4226
  })();
3737
4227
 
3738
4228
  window.TinyBasicsEs = __webpack_exports__;