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
|
||||
|
||||
67
index.html
67
index.html
@ -1,37 +1,36 @@
|
||||
<!doctype html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/assets/favicon.svg" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
|
||||
/>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/assets/favicon.svg" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
|
||||
/>
|
||||
|
||||
<meta
|
||||
name="description"
|
||||
content="Airbnb co-hosting and property management."
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="/assets/favicon192.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta
|
||||
name="apple-mobile-web-app-status-bar-style"
|
||||
content="black-translucent"
|
||||
/>
|
||||
<link rel="stylesheet" href="/global.css" />
|
||||
<link rel="stylesheet" href="/fonts.css" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>The Hideout</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root" class="th-root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
<meta
|
||||
name="description"
|
||||
content="Airbnb co-hosting and property management."
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="/assets/favicon192.png" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta
|
||||
name="apple-mobile-web-app-status-bar-style"
|
||||
content="black-translucent"
|
||||
/>
|
||||
<link rel="stylesheet" href="/fonts.css" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<title>The Hideout</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root" class="th-root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
"react-scroll": "^1.9.3",
|
||||
"react-turnstile": "^1.1.4",
|
||||
"sass": "^1.86.3",
|
||||
"simplebar-react": "^3.3.2",
|
||||
"vite": "^6.2.5",
|
||||
"vite-plugin-svgo": "^2.0.0",
|
||||
"vite-plugin-svgr": "^4.5.0",
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
useSettingsContext,
|
||||
} from "./contexts/SettingsContext";
|
||||
const apiUrl = import.meta.env.VITE_API_URL;
|
||||
import "./global.css";
|
||||
|
||||
// Component that handles image loading after API data is fetched
|
||||
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 { Layout } from "antd";
|
||||
import ContentRenderer from "./ContentRenderer";
|
||||
import HeaderLogo from "./HeaderLogo";
|
||||
import ScrollIcon from "../icons/ScrollIcon";
|
||||
@ -9,8 +8,8 @@ import ImageCarousel from "./ImageCarousel";
|
||||
import CloseButton from "./CloseButton";
|
||||
import MenuButton from "./MenuButton";
|
||||
import { useSettingsContext } from "../contexts/SettingsContext";
|
||||
|
||||
const { Content } = Layout;
|
||||
import Simplebar from "simplebar-react";
|
||||
import "simplebar-react/dist/simplebar.min.css";
|
||||
|
||||
const Page = ({
|
||||
pageData,
|
||||
@ -29,10 +28,11 @@ const Page = ({
|
||||
!pageData.showProperties &&
|
||||
!pageData.showContactForm &&
|
||||
!pageData.hideMobileImage;
|
||||
const contentRef = useRef(null);
|
||||
const scrollRef = useRef(null);
|
||||
const [isImageShrunk, setIsImageShrunk] = useState(false);
|
||||
const [safariBlurToggle, setSafariBlurToggle] = useState(false);
|
||||
const shrinkDistance = 1;
|
||||
const [hasVerticalScrollbar, setHasVerticalScrollbar] = useState(false);
|
||||
|
||||
const settings = useSettingsContext();
|
||||
const themes = settings?.themes || [];
|
||||
@ -43,8 +43,9 @@ const Page = ({
|
||||
|
||||
// Reset scroll position and image state when visible becomes false
|
||||
useEffect(() => {
|
||||
if (!visible && contentRef.current) {
|
||||
contentRef.current.scrollTop = 0;
|
||||
if (!visible && scrollRef.current) {
|
||||
const el = scrollRef.current.getScrollElement();
|
||||
el.scrollTop = 0;
|
||||
setIsImageShrunk(false);
|
||||
}
|
||||
}, [visible]);
|
||||
@ -83,14 +84,50 @@ const Page = ({
|
||||
};
|
||||
}, [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(() => {
|
||||
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 = () => {
|
||||
// Don't shrink image if page is not visible
|
||||
if (!visible) return;
|
||||
|
||||
const el = contentRef.current;
|
||||
const el = scrollRef.current.getScrollElement();
|
||||
const scrollTop = el.scrollTop;
|
||||
const scrollHeight = el.scrollHeight;
|
||||
const clientHeight = el.clientHeight;
|
||||
@ -118,7 +155,7 @@ const Page = ({
|
||||
}
|
||||
};
|
||||
|
||||
const el = contentRef.current;
|
||||
const el = scrollRef.current.getScrollElement();
|
||||
el.addEventListener("scroll", handleScroll);
|
||||
|
||||
return () => {
|
||||
@ -172,22 +209,34 @@ const Page = ({
|
||||
<ImageCarousel page={pageData} className="th-mobile-image" />
|
||||
</div>
|
||||
)}
|
||||
<Content
|
||||
|
||||
<div
|
||||
className={`th-page-content${
|
||||
visible == false ? " th-page-content-hidden" : ""
|
||||
}${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 ${
|
||||
mobileImage ? " th-content-container-mobile-image" : ""
|
||||
className={`th-content-container-wrapper${
|
||||
hasVerticalScrollbar ? " th-content-container-wrapper-scroll" : ""
|
||||
}`}
|
||||
>
|
||||
<ContentRenderer content={pageData?.content} />
|
||||
<div
|
||||
className={`th-content-container ${
|
||||
mobileImage ? " th-content-container-mobile-image" : ""
|
||||
}`}
|
||||
>
|
||||
<ContentRenderer content={pageData?.content} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Content>
|
||||
</Simplebar>
|
||||
</div>
|
||||
|
||||
{pageData?.showScroll == true && <ScrollIcon visible={visible} />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -303,7 +303,8 @@ hr.th-divider {
|
||||
flex-grow: 3;
|
||||
padding: 0 var(--th-page-padding);
|
||||
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%;
|
||||
color: var(--th-textColor);
|
||||
display: flex;
|
||||
@ -311,9 +312,36 @@ hr.th-divider {
|
||||
align-items: start;
|
||||
text-align: left;
|
||||
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 {
|
||||
@ -450,6 +478,10 @@ hr.th-divider {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.th-content-container-wrapper-scroll {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
/* App component styles */
|
||||
.th-app-container {
|
||||
height: var(--unit-100vh);
|
||||
@ -755,8 +787,10 @@ hr.th-divider {
|
||||
overflow: hidden; /* Prevent overflow */
|
||||
border-bottom: 1px solid var(--th-textColor);
|
||||
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;
|
||||
min-height: 260px;
|
||||
max-height: 260px;
|
||||
}
|
||||
|
||||
@ -765,6 +799,7 @@ hr.th-divider {
|
||||
}
|
||||
|
||||
.th-page-mobile-image-wrapper-shrunk {
|
||||
min-height: 110px;
|
||||
max-height: 110px;
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ export default defineConfig({
|
||||
plugins: [react(), svgo(), svgr()],
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
allowedHosts: ["thehideout.tombutcher.work"],
|
||||
allowedHosts: ["dev.tombutcher.work"],
|
||||
},
|
||||
base: "/",
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user