Compare commits
3 Commits
32e085c190
...
9561887e4b
| Author | SHA1 | Date | |
|---|---|---|---|
| 9561887e4b | |||
| 040d7133f6 | |||
| f1c57dde3b |
@ -1,2 +1,2 @@
|
|||||||
VITE_API_URL=https://thehideout.tombutcher.work/api
|
VITE_API_URL=https://dev.tombutcher.work/api
|
||||||
VITE_TURNSTILE_KEY=0x4AAAAAAB2dBq6i8m4kYzDm
|
VITE_TURNSTILE_KEY=0x4AAAAAAB2dBq6i8m4kYzDm
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
@ -24,7 +24,6 @@
|
|||||||
name="apple-mobile-web-app-status-bar-style"
|
name="apple-mobile-web-app-status-bar-style"
|
||||||
content="black-translucent"
|
content="black-translucent"
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href="/global.css" />
|
|
||||||
<link rel="stylesheet" href="/fonts.css" />
|
<link rel="stylesheet" href="/fonts.css" />
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<title>The Hideout</title>
|
<title>The Hideout</title>
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
"react-scroll": "^1.9.3",
|
"react-scroll": "^1.9.3",
|
||||||
"react-turnstile": "^1.1.4",
|
"react-turnstile": "^1.1.4",
|
||||||
"sass": "^1.86.3",
|
"sass": "^1.86.3",
|
||||||
|
"simplebar-react": "^3.3.2",
|
||||||
"vite": "^6.2.5",
|
"vite": "^6.2.5",
|
||||||
"vite-plugin-svgo": "^2.0.0",
|
"vite-plugin-svgo": "^2.0.0",
|
||||||
"vite-plugin-svgr": "^4.5.0",
|
"vite-plugin-svgr": "^4.5.0",
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
useSettingsContext,
|
useSettingsContext,
|
||||||
} from "./contexts/SettingsContext";
|
} from "./contexts/SettingsContext";
|
||||||
const apiUrl = import.meta.env.VITE_API_URL;
|
const apiUrl = import.meta.env.VITE_API_URL;
|
||||||
|
import "./global.css";
|
||||||
|
|
||||||
// Component that handles image loading after API data is fetched
|
// Component that handles image loading after API data is fetched
|
||||||
const AppContent = ({ pages, properties, images }) => {
|
const AppContent = ({ pages, properties, images }) => {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState, useCallback } from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Layout } from "antd";
|
|
||||||
import ContentRenderer from "./ContentRenderer";
|
import ContentRenderer from "./ContentRenderer";
|
||||||
import HeaderLogo from "./HeaderLogo";
|
import HeaderLogo from "./HeaderLogo";
|
||||||
import ScrollIcon from "../icons/ScrollIcon";
|
import ScrollIcon from "../icons/ScrollIcon";
|
||||||
@ -9,8 +8,8 @@ import ImageCarousel from "./ImageCarousel";
|
|||||||
import CloseButton from "./CloseButton";
|
import CloseButton from "./CloseButton";
|
||||||
import MenuButton from "./MenuButton";
|
import MenuButton from "./MenuButton";
|
||||||
import { useSettingsContext } from "../contexts/SettingsContext";
|
import { useSettingsContext } from "../contexts/SettingsContext";
|
||||||
|
import Simplebar from "simplebar-react";
|
||||||
const { Content } = Layout;
|
import "simplebar-react/dist/simplebar.min.css";
|
||||||
|
|
||||||
const Page = ({
|
const Page = ({
|
||||||
pageData,
|
pageData,
|
||||||
@ -29,10 +28,11 @@ const Page = ({
|
|||||||
!pageData.showProperties &&
|
!pageData.showProperties &&
|
||||||
!pageData.showContactForm &&
|
!pageData.showContactForm &&
|
||||||
!pageData.hideMobileImage;
|
!pageData.hideMobileImage;
|
||||||
const contentRef = useRef(null);
|
const scrollRef = useRef(null);
|
||||||
const [isImageShrunk, setIsImageShrunk] = useState(false);
|
const [isImageShrunk, setIsImageShrunk] = useState(false);
|
||||||
const [safariBlurToggle, setSafariBlurToggle] = useState(false);
|
const [safariBlurToggle, setSafariBlurToggle] = useState(false);
|
||||||
const shrinkDistance = 1;
|
const shrinkDistance = 1;
|
||||||
|
const [hasVerticalScrollbar, setHasVerticalScrollbar] = useState(false);
|
||||||
|
|
||||||
const settings = useSettingsContext();
|
const settings = useSettingsContext();
|
||||||
const themes = settings?.themes || [];
|
const themes = settings?.themes || [];
|
||||||
@ -43,8 +43,9 @@ const Page = ({
|
|||||||
|
|
||||||
// Reset scroll position and image state when visible becomes false
|
// Reset scroll position and image state when visible becomes false
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visible && contentRef.current) {
|
if (!visible && scrollRef.current) {
|
||||||
contentRef.current.scrollTop = 0;
|
const el = scrollRef.current.getScrollElement();
|
||||||
|
el.scrollTop = 0;
|
||||||
setIsImageShrunk(false);
|
setIsImageShrunk(false);
|
||||||
}
|
}
|
||||||
}, [visible]);
|
}, [visible]);
|
||||||
@ -83,14 +84,50 @@ const Page = ({
|
|||||||
};
|
};
|
||||||
}, [isImageShrunk, mobileImage]);
|
}, [isImageShrunk, mobileImage]);
|
||||||
|
|
||||||
|
const checkOverflow = useCallback(() => {
|
||||||
|
if (!scrollRef.current) return;
|
||||||
|
const el = scrollRef.current.getScrollElement();
|
||||||
|
if (!el) return;
|
||||||
|
const canScroll = el.scrollHeight > el.clientHeight;
|
||||||
|
setHasVerticalScrollbar(canScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Recalculate overflow on mount, when layout/content changes, and on resize
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!contentRef.current || !mobileImage) return;
|
checkOverflow();
|
||||||
|
}, [
|
||||||
|
checkOverflow,
|
||||||
|
pageData?.content,
|
||||||
|
visible,
|
||||||
|
isLargeMobile,
|
||||||
|
isMobile,
|
||||||
|
mobileImage,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Observe size changes of the scroll container
|
||||||
|
useEffect(() => {
|
||||||
|
if (!scrollRef.current) return;
|
||||||
|
const el = scrollRef.current.getScrollElement();
|
||||||
|
if (!el) return;
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
checkOverflow();
|
||||||
|
});
|
||||||
|
resizeObserver.observe(el);
|
||||||
|
window.addEventListener("resize", checkOverflow);
|
||||||
|
return () => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
window.removeEventListener("resize", checkOverflow);
|
||||||
|
};
|
||||||
|
}, [checkOverflow]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!scrollRef.current || !mobileImage) return;
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
// Don't shrink image if page is not visible
|
// Don't shrink image if page is not visible
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
|
|
||||||
const el = contentRef.current;
|
const el = scrollRef.current.getScrollElement();
|
||||||
const scrollTop = el.scrollTop;
|
const scrollTop = el.scrollTop;
|
||||||
const scrollHeight = el.scrollHeight;
|
const scrollHeight = el.scrollHeight;
|
||||||
const clientHeight = el.clientHeight;
|
const clientHeight = el.clientHeight;
|
||||||
@ -118,7 +155,7 @@ const Page = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const el = contentRef.current;
|
const el = scrollRef.current.getScrollElement();
|
||||||
el.addEventListener("scroll", handleScroll);
|
el.addEventListener("scroll", handleScroll);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -172,13 +209,23 @@ const Page = ({
|
|||||||
<ImageCarousel page={pageData} className="th-mobile-image" />
|
<ImageCarousel page={pageData} className="th-mobile-image" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Content
|
|
||||||
|
<div
|
||||||
className={`th-page-content${
|
className={`th-page-content${
|
||||||
visible == false ? " th-page-content-hidden" : ""
|
visible == false ? " th-page-content-hidden" : ""
|
||||||
}${isLargeMobile ? " th-page-content-mobile" : ""}`}
|
}${isLargeMobile ? " th-page-content-mobile" : ""}`}
|
||||||
ref={contentRef}
|
|
||||||
>
|
>
|
||||||
<div className="th-content-container-wrapper">
|
<Simplebar
|
||||||
|
className="th-content-scroll"
|
||||||
|
ref={scrollRef}
|
||||||
|
forceVisible="y"
|
||||||
|
autoHide={false}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`th-content-container-wrapper${
|
||||||
|
hasVerticalScrollbar ? " th-content-container-wrapper-scroll" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={`th-content-container ${
|
className={`th-content-container ${
|
||||||
mobileImage ? " th-content-container-mobile-image" : ""
|
mobileImage ? " th-content-container-mobile-image" : ""
|
||||||
@ -187,7 +234,9 @@ const Page = ({
|
|||||||
<ContentRenderer content={pageData?.content} />
|
<ContentRenderer content={pageData?.content} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Content>
|
</Simplebar>
|
||||||
|
</div>
|
||||||
|
|
||||||
{pageData?.showScroll == true && <ScrollIcon visible={visible} />}
|
{pageData?.showScroll == true && <ScrollIcon visible={visible} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -303,7 +303,8 @@ hr.th-divider {
|
|||||||
flex-grow: 3;
|
flex-grow: 3;
|
||||||
padding: 0 var(--th-page-padding);
|
padding: 0 var(--th-page-padding);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: var(--unit-100vh); /* Use fixed height instead of min-height */
|
height: 100%; /* Use fixed height instead of min-height */
|
||||||
|
min-height: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--th-textColor);
|
color: var(--th-textColor);
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -311,9 +312,36 @@ hr.th-divider {
|
|||||||
align-items: start;
|
align-items: start;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
transition: opacity 0.3s ease-in-out;
|
transition: opacity 0.3s ease-in-out;
|
||||||
/* Prevent layout shifts */
|
}
|
||||||
contain: layout style;
|
|
||||||
overflow-y: scroll;
|
.th-content-scroll {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simplebar-scrollbar:before {
|
||||||
|
background: var(--th-textColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.simplebar-track.simplebar-vertical {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simplebar-scrollbar.simplebar-visible:before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure Simplebar fills and scrolls correctly */
|
||||||
|
.th-content-scroll .simplebar-content-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.th-content-scroll .simplebar-content {
|
||||||
|
min-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.th-page-content-mobile {
|
.th-page-content-mobile {
|
||||||
@ -450,6 +478,10 @@ hr.th-divider {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.th-content-container-wrapper-scroll {
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
/* App component styles */
|
/* App component styles */
|
||||||
.th-app-container {
|
.th-app-container {
|
||||||
height: var(--unit-100vh);
|
height: var(--unit-100vh);
|
||||||
@ -755,8 +787,10 @@ hr.th-divider {
|
|||||||
overflow: hidden; /* Prevent overflow */
|
overflow: hidden; /* Prevent overflow */
|
||||||
border-bottom: 1px solid var(--th-textColor);
|
border-bottom: 1px solid var(--th-textColor);
|
||||||
transition: opacity 0.3s ease-in-out,
|
transition: opacity 0.3s ease-in-out,
|
||||||
max-height 0.75s cubic-bezier(0, 0.5, 0.5, 1);
|
max-height 0.75s cubic-bezier(0, 0.5, 0.5, 1),
|
||||||
|
min-height 0.75s cubic-bezier(0, 0.5, 0.5, 1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
min-height: 260px;
|
||||||
max-height: 260px;
|
max-height: 260px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -765,6 +799,7 @@ hr.th-divider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.th-page-mobile-image-wrapper-shrunk {
|
.th-page-mobile-image-wrapper-shrunk {
|
||||||
|
min-height: 110px;
|
||||||
max-height: 110px;
|
max-height: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ export default defineConfig({
|
|||||||
plugins: [react(), svgo(), svgr()],
|
plugins: [react(), svgo(), svgr()],
|
||||||
server: {
|
server: {
|
||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
allowedHosts: ["thehideout.tombutcher.work"],
|
allowedHosts: ["dev.tombutcher.work"],
|
||||||
},
|
},
|
||||||
base: "/",
|
base: "/",
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user