custom scrollbar implemented
Some checks failed
Rebuild signaller for deprived.dev to rebuild site / test_service (push) Failing after 0s

This commit is contained in:
BOTAlex 2025-08-20 02:26:40 +02:00
parent c099b9ce9f
commit 7a8d61d598
3 changed files with 116 additions and 83 deletions

View file

@ -7,7 +7,7 @@
<div class="flex flex-col items-center justify-center w-full h-full"> <div class="flex flex-col items-center justify-center w-full h-full">
<div class="flex flex-col w-72"> <div class="flex flex-col w-72">
<div class="w-full h-72 bg-lime-200"></div> <div class="w-full h-72 bg-lime-200"></div>
<CustomScrollBar overflowX="scroll" overflowY="hidden" Class="h-26"> <CustomScrollBar overflowX="scroll" overflowY="hidden" Class="">
<div class="flex w-full gap-4"> <div class="flex w-full gap-4">
{#each { length: 4 } as i} {#each { length: 4 } as i}
<img <img

View file

@ -11,8 +11,8 @@
import DeprivedLogo from "$lib/images/DeprivedLogo.svelte"; import DeprivedLogo from "$lib/images/DeprivedLogo.svelte";
import HamburgerMenuIcon from "$lib/images/HamburgerMenuIcon.svelte"; import HamburgerMenuIcon from "$lib/images/HamburgerMenuIcon.svelte";
const footerCollapseThreshold: string = "1000px"; const footerCollapseThreshold: string = "40rem";
const headerCollapseThreshold: string = "1000px"; const headerCollapseThreshold: string = "40rem";
let footerCollapse: boolean; let footerCollapse: boolean;
let isMobile: boolean = $state(false); let isMobile: boolean = $state(false);
@ -31,6 +31,7 @@
import { onMount } from "svelte"; import { onMount } from "svelte";
import Zooter from "./comps/Zooter.svelte"; import Zooter from "./comps/Zooter.svelte";
import CustomScrollBar from "./comps/CustomScrollBar.svelte";
onMount(async () => { onMount(async () => {
const lock = document.createElement("meta"); const lock = document.createElement("meta");
@ -82,7 +83,14 @@
bind:matches={isMobile} bind:matches={isMobile}
/> />
<div class="flex flex-col justify-between min-h-screen bg-base-200 p-0"> <CustomScrollBar
overflowX="hidden"
overflowY="auto"
Class="h-screen"
requireAbsolute={true}
hideOnMobile={true}
>
<div class="flex flex-col justify-between min-h-screen bg-base-200 p-0">
<header class="{hideOnPrint ? 'hide-on-print' : ''} bg-base-300"> <header class="{hideOnPrint ? 'hide-on-print' : ''} bg-base-300">
<div class="nav-bar pr-4"> <div class="nav-bar pr-4">
{#if !isMobile} {#if !isMobile}
@ -161,7 +169,8 @@
</div> </div>
<Zooter bind:hideOnPrint /> <Zooter bind:hideOnPrint />
</div> </div>
</CustomScrollBar>
{#if footerCollapse} {#if footerCollapse}
<style> <style>

View file

@ -1,15 +1,18 @@
<script lang="ts"> <script lang="ts">
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import MediaQuery from "svelte-media-queries";
// Public props // Public props
export let overflowX: "auto" | "scroll" | "hidden" = "hidden"; export let overflowX: "auto" | "scroll" | "hidden" = "hidden";
export let overflowY: "auto" | "scroll" | "hidden" = "auto"; export let overflowY: "auto" | "scroll" | "hidden" = "auto";
export let hideOnMobile = false; // True if hide scrollbar when mobile detected
// Visual tuning // Visual tuning
export let thickness = 12; // px export let thickness = 12; // px
export let padding = 0; // px, space around track inside the overlay export let padding = 0; // px, space around track inside the overlay
export let minThumb = 24; // px export let minThumb = 10; // px
export let contentPadding = 16; // px, inner padding for your content export let contentPadding = 0; // px, inner padding for your content
export let Class = ""; // Extra classes for the scrollbar export let Class = ""; // Extra classes for the scrollbar
// Styling (customize freely) // Styling (customize freely)
@ -18,9 +21,11 @@
export let trackOpacity = 0.55; export let trackOpacity = 0.55;
export let thumbClass = export let thumbClass =
"bg-black border-x-1 opacity-50 border-white text-xs text-center text-opacity-50 overflow-hidden flex items-center text-nowrap justify-center"; "bg-black opacity-50 border-white text-xs text-center text-opacity-50 overflow-hidden flex items-center text-nowrap justify-center";
export let thumbLength = 20; // px. doesn't work for some reason, idk export let thumbLength = 20; // px. doesn't work for some reason, idk
export let requireAbsolute = false; // Some needs absolute for some reason. idk
let viewport: HTMLDivElement; let viewport: HTMLDivElement;
let vBar: HTMLDivElement; // vertical bar container let vBar: HTMLDivElement; // vertical bar container
let hBar: HTMLDivElement; // horizontal bar container let hBar: HTMLDivElement; // horizontal bar container
@ -29,6 +34,7 @@
let showBarY = false; let showBarY = false;
let showBarX = false; let showBarX = false;
let isMobile = false;
// ——— utils // ——— utils
const sMaxY = () => const sMaxY = () =>
@ -41,9 +47,13 @@
function updateVisibility() { function updateVisibility() {
showBarY = showBarY =
overflowY !== "hidden" && viewport.scrollHeight > viewport.clientHeight; overflowY !== "hidden" &&
viewport.scrollHeight > viewport.clientHeight &&
!(hideOnMobile && isMobile);
showBarX = showBarX =
overflowX !== "hidden" && viewport.scrollWidth > viewport.clientWidth; overflowX !== "hidden" &&
viewport.scrollWidth > viewport.clientWidth &&
!(hideOnMobile && isMobile);
} }
function updateVerticalThumb() { function updateVerticalThumb() {
@ -268,15 +278,25 @@
$: pb = contentPadding + (showBarX ? thickness + padding * 2 : 0); $: pb = contentPadding + (showBarX ? thickness + padding * 2 : 0);
</script> </script>
<MediaQuery query="(max-width: 40rem)" bind:matches={isMobile} />
<!-- svelte-ignore element_invalid_self_closing_tag --> <!-- svelte-ignore element_invalid_self_closing_tag -->
<!-- svelte-ignore a11y_role_has_required_aria_props --> <!-- svelte-ignore a11y_role_has_required_aria_props -->
<!-- Wrapper --> <!-- Wrapper -->
<div class="relative overflow-hidden {Class}"> <div
class="relative {overflowY == 'hidden'
? ''
: 'overflow-y-hidden'} {overflowX == 'hidden'
? ''
: 'overflow-x-hidden'} {Class}"
>
<!-- The real, native scrolling area (we hide its native scrollbar) --> <!-- The real, native scrolling area (we hide its native scrollbar) -->
<div <div
bind:this={viewport} bind:this={viewport}
class="csb-viewport absolute inset-0 overflow-auto focus:outline-none" class="csb-viewport {requireAbsolute
? 'absolute'
: ''} inset-0 overflow-auto focus:outline-none"
style=" style="
padding: {contentPadding}px {pr}px {pb}px {contentPadding}px; padding: {contentPadding}px {pr}px {pb}px {contentPadding}px;
overscroll-behavior: contain; overscroll-behavior: contain;
@ -292,7 +312,7 @@
{#if showBarY} {#if showBarY}
<div <div
bind:this={vBar} bind:this={vBar}
class="absolute" class="absolute bg-base-200"
style=" style="
top: {padding}px; top: {padding}px;
bottom: {padding}px; bottom: {padding}px;
@ -304,7 +324,7 @@
> >
<div class="absolute inset-0 corner-border-container"> <div class="absolute inset-0 corner-border-container">
<div <div
class="transition-opacity {trackClass} w-full" class="transition-opacity {trackClass} w-full h-full"
style=" style="
pointer-events: auto; pointer-events: auto;
{trackStyle} {trackStyle}
@ -318,14 +338,18 @@
role="scrollbar" role="scrollbar"
aria-orientation="vertical" aria-orientation="vertical"
tabindex="0" tabindex="0"
class={`absolute left-[2px] right-[2px] rounded-full cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`} class={`absolute border-y-2 left-0 right-0 rounded-none cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`}
style="height: {thumbLength}px; pointer-events: auto; touch-action: none;" style="height: {thumbLength}px; pointer-events: auto; touch-action: none;"
on:pointerdown={onVPointerDown} on:pointerdown={onVPointerDown}
on:pointermove={onVPointerMove} on:pointermove={onVPointerMove}
on:pointerup={endVDrag} on:pointerup={endVDrag}
on:pointercancel={endVDrag} on:pointercancel={endVDrag}
on:keydown={onVKeyDown} on:keydown={onVKeyDown}
></div> >
<span class="rotate-90">
-------------------------------------------------Scroll-------------------------------------------------
</span>
</div>
</div> </div>
{/if} {/if}
@ -358,7 +382,7 @@
role="scrollbar" role="scrollbar"
aria-orientation="horizontal" aria-orientation="horizontal"
tabindex="0" tabindex="0"
class={`absolute top-0 bottom-0 rounded-none cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`} class={`absolute border-x-2 top-0 bottom-0 rounded-none cursor-grab active:cursor-grabbing focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-500 ${thumbClass}`}
style="width: {thumbLength}px; pointer-events: auto; touch-action: none;" style="width: {thumbLength}px; pointer-events: auto; touch-action: none;"
on:pointerdown={onHPointerDown} on:pointerdown={onHPointerDown}
on:pointermove={onHPointerMove} on:pointermove={onHPointerMove}