style changes
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<link rel="stylesheet" href="reset.css">
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|||||||
10
frontend/package-lock.json
generated
10
frontend/package-lock.json
generated
@@ -11,6 +11,7 @@
|
|||||||
"@types/react-router": "^5.1.20",
|
"@types/react-router": "^5.1.20",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
|
"react-icons": "^5.5.0",
|
||||||
"react-router-dom": "^7.8.1"
|
"react-router-dom": "^7.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -2922,6 +2923,15 @@
|
|||||||
"react": "^19.1.1"
|
"react": "^19.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-icons": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.17.0",
|
"version": "0.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"@types/react-router": "^5.1.20",
|
"@types/react-router": "^5.1.20",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
|
"react-icons": "^5.5.0",
|
||||||
"react-router-dom": "^7.8.1"
|
"react-router-dom": "^7.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
/*TODO: Implement the contact form functionality*/
|
|
||||||
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="contact-me">
|
|
||||||
<div class="opening">
|
|
||||||
<i class="fa-solid fa-arrow-pointer" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<form method="post" id="contactme-form">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ contactme_form }}
|
|
||||||
<input type="submit" value="Submit">
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="cover"></div>
|
|
||||||
<div class="triangle"></div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<script src="{% static 'home/js/global/contact-me.js' %}"></script>
|
|
||||||
|
|
||||||
|
|
||||||
contact-me.js:
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
$("#contactme-form").submit(function (event) {
|
|
||||||
event.preventDefault(); // Prevent normal form submission
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: "/submit-contactme/", // URL of the Django view
|
|
||||||
type: "POST",
|
|
||||||
data: $(this).serialize(), // Serialize form data
|
|
||||||
success: function (response) {
|
|
||||||
if (response.success) {
|
|
||||||
close_contact();
|
|
||||||
|
|
||||||
$("#contactme-form .success-form-alert").fadeIn();
|
|
||||||
$("#contactme-form")[0].reset(); // Clear the form
|
|
||||||
|
|
||||||
alert("Zpráva odeslaná!")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (response) {
|
|
||||||
alert("Zpráva nebyla odeslaná, zkontrolujte si připojení k internetu nebo naskytl u nás problém :(")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#contactme-form .success-form .close").click(function () {
|
|
||||||
$("#contactme-form .success-form-alert").fadeOut();
|
|
||||||
});
|
|
||||||
|
|
||||||
var opened_contact = false;
|
|
||||||
|
|
||||||
$(document).on("click", ".contact-me .opening", function () {
|
|
||||||
console.log("toggle mail");
|
|
||||||
|
|
||||||
const opening = $(".contact-me .opening");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Check if we're opening or closing
|
|
||||||
if (opened_contact === false) {
|
|
||||||
// Toggle rotation
|
|
||||||
opening.toggleClass('rotate-opening');
|
|
||||||
|
|
||||||
// Wait for the rotation to finish
|
|
||||||
setTimeout(function() {
|
|
||||||
$(".contact-me .content").addClass('content-moveup-index');
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
$(".contact-me .content")[0].offsetHeight;
|
|
||||||
|
|
||||||
$(".contact-me .content").addClass('content-moveup');
|
|
||||||
}, 1000); // Small delay to trigger transition
|
|
||||||
|
|
||||||
opened_contact = true;
|
|
||||||
} else {
|
|
||||||
close_contact();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function close_contact(){
|
|
||||||
$(".contact-me .content").removeClass('content-moveup');
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
$(".contact-me .content").toggleClass('content-moveup-index');
|
|
||||||
$(".contact-me .opening").toggleClass('rotate-opening');
|
|
||||||
}, 700);
|
|
||||||
|
|
||||||
opened_contact = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,82 +1,64 @@
|
|||||||
import React, { useState } from "react"
|
import React, { useState, useRef } from "react"
|
||||||
import styles from "./contact-me.module.css"
|
import styles from "./contact-me.module.css"
|
||||||
|
import { LuMousePointerClick } from "react-icons/lu";
|
||||||
interface ContactFormResponse {
|
|
||||||
success: boolean
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ContactMeForm() {
|
export default function ContactMeForm() {
|
||||||
const [opened, setOpened] = useState(false)
|
const [opened, setOpened] = useState(false)
|
||||||
const [formData, setFormData] = useState({ message: "", email: "" })
|
const [contentMoveUp, setContentMoveUp] = useState(false)
|
||||||
const [loading, setLoading] = useState(false)
|
const [openingBehind, setOpeningBehind] = useState(false)
|
||||||
const [success, setSuccess] = useState(false)
|
const [success, setSuccess] = useState(false)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const openingRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
// form submission logic here
|
||||||
|
}
|
||||||
|
|
||||||
const toggleOpen = () => {
|
const toggleOpen = () => {
|
||||||
setOpened((prev) => !prev)
|
if (!opened) {
|
||||||
}
|
setOpened(true)
|
||||||
|
setOpeningBehind(false)
|
||||||
const handleChange = (
|
setContentMoveUp(false)
|
||||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
// Wait for the rotate-opening animation to finish before moving content up
|
||||||
) => {
|
// The actual moveUp will be handled in onTransitionEnd
|
||||||
const { name, value } = e.target
|
} else {
|
||||||
setFormData((prev) => ({ ...prev, [name]: value }))
|
setContentMoveUp(false)
|
||||||
}
|
setOpeningBehind(false)
|
||||||
|
setTimeout(() => setOpened(false), 1000) // match transition duration
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
setLoading(true)
|
|
||||||
setError(null)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch("/submit-contactme/", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-CSRFToken": getCSRFToken(),
|
|
||||||
},
|
|
||||||
body: JSON.stringify(formData),
|
|
||||||
})
|
|
||||||
|
|
||||||
const data: ContactFormResponse = await response.json()
|
|
||||||
|
|
||||||
if (data.success) {
|
|
||||||
setSuccess(true)
|
|
||||||
setFormData({ message: "", email: "" })
|
|
||||||
} else {
|
|
||||||
setError("Zpráva nebyla odeslaná.")
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError("Chyba připojení nebo serveru.")
|
|
||||||
} finally {
|
|
||||||
setLoading(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility to read CSRF token from cookies (Django)
|
const handleTransitionEnd = (e: React.TransitionEvent<HTMLDivElement>) => {
|
||||||
const getCSRFToken = (): string => {
|
if (opened && e.propertyName === "transform") {
|
||||||
const match = document.cookie.match(/csrftoken=([^;]+)/)
|
setContentMoveUp(true)
|
||||||
return match ? match[1] : ""
|
// Move the opening behind after the animation
|
||||||
|
setTimeout(() => setOpeningBehind(true), 10)
|
||||||
|
}
|
||||||
|
if (!opened && e.propertyName === "transform") {
|
||||||
|
setOpeningBehind(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles["contact-me"]}>
|
<div className={styles["contact-me"]}>
|
||||||
<div
|
<div
|
||||||
|
ref={openingRef}
|
||||||
className={
|
className={
|
||||||
opened
|
[
|
||||||
? `${styles.opening} ${styles["rotate-opening"] || ""}`
|
styles.opening,
|
||||||
: styles.opening
|
opened ? styles["rotate-opening"] : "",
|
||||||
|
openingBehind ? styles["opening-behind"] : ""
|
||||||
|
].filter(Boolean).join(" ")
|
||||||
}
|
}
|
||||||
onClick={toggleOpen}
|
onClick={toggleOpen}
|
||||||
|
onTransitionEnd={handleTransitionEnd}
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-arrow-pointer" aria-hidden="true"></i>
|
<LuMousePointerClick/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
opened
|
contentMoveUp
|
||||||
? `${styles.content} ${styles["content-moveup"] || ""}`
|
? `${styles.content} ${styles["content-moveup"]}`
|
||||||
: styles.content
|
: styles.content
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -85,27 +67,15 @@ export default function ContactMeForm() {
|
|||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
placeholder="Váš email"
|
placeholder="Váš email"
|
||||||
value={formData.email}
|
|
||||||
onChange={handleChange}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
name="message"
|
name="message"
|
||||||
placeholder="Vaše zpráva"
|
placeholder="Vaše zpráva"
|
||||||
value={formData.message}
|
|
||||||
onChange={handleChange}
|
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<input type="submit" value={loading ? "Odesílám..." : "Odeslat"} />
|
<input type="submit"/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{success && (
|
|
||||||
<div className={styles.successFormAlert}>
|
|
||||||
<span>Zpráva odeslaná!</span>
|
|
||||||
<button onClick={() => setSuccess(false)}>×</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{error && <div className={styles.errorFormAlert}>{error}</div>}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.cover}></div>
|
<div className={styles.cover}></div>
|
||||||
|
|||||||
@@ -35,6 +35,11 @@
|
|||||||
transform: rotateX(180deg);
|
transform: rotateX(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opening svg{
|
||||||
|
margin: auto;
|
||||||
|
font-size: 3em;
|
||||||
|
margin-top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.contact-me .content {
|
.contact-me .content {
|
||||||
@@ -89,7 +94,7 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.opening-behind { z-index: 0 !important; }
|
||||||
|
|
||||||
.contact-me .cover {
|
.contact-me .cover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import styles from "./HomeNav.module.css"
|
import styles from "./HomeNav.module.css"
|
||||||
|
import { FaBars } from "react-icons/fa";
|
||||||
|
|
||||||
export default function HomeNav() {
|
export default function HomeNav() {
|
||||||
const [navOpen, setNavOpen] = useState(false)
|
const [navOpen, setNavOpen] = useState(false)
|
||||||
@@ -8,11 +9,7 @@ export default function HomeNav() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={styles.nav}>
|
<nav className={styles.nav}>
|
||||||
<i
|
<FaBars className={styles.toggle} onClick={toggleNav} />
|
||||||
id="toggle-nav"
|
|
||||||
className={`fa-solid fa-bars ${styles.toggle}`}
|
|
||||||
onClick={toggleNav}
|
|
||||||
></i>
|
|
||||||
|
|
||||||
<ul className={`${styles.navList} ${navOpen ? styles.open : ""}`}>
|
<ul className={`${styles.navList} ${navOpen ? styles.open : ""}`}>
|
||||||
<li id="nav-logo" className={styles.logo}>
|
<li id="nav-logo" className={styles.logo}>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #c2a67d;
|
background-color: #c2a67d;
|
||||||
|
color: #5e5747;
|
||||||
|
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
|
|
||||||
@@ -29,6 +30,13 @@
|
|||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.portfolio div span svg{
|
||||||
|
font-size: 5em;
|
||||||
|
cursor: pointer;
|
||||||
|
animation: shake 0.4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@keyframes shake {
|
@keyframes shake {
|
||||||
0% { transform: translateX(0); }
|
0% { transform: translateX(0); }
|
||||||
@@ -55,9 +63,9 @@
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
top: -4.7em;
|
top: -3.7em;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding: 0 3em;
|
padding: 1em 3em;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
background-color: #cdc19c;
|
background-color: #cdc19c;
|
||||||
color: #5e5747;
|
color: #5e5747;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import styles from "./Portfolio.module.css"
|
import styles from "./Portfolio.module.css"
|
||||||
|
import { LuMousePointerClick } from "react-icons/lu";
|
||||||
|
|
||||||
interface PortfolioItem {
|
interface PortfolioItem {
|
||||||
href: string
|
href: string
|
||||||
@@ -46,7 +47,7 @@ export default function Portfolio() {
|
|||||||
}
|
}
|
||||||
onClick={toggleDoor}
|
onClick={toggleDoor}
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-arrow-pointer">fix missing font awesome</i>
|
<LuMousePointerClick/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{portfolioItems.map((item, index) => (
|
{portfolioItems.map((item, index) => (
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ export default function HomeLayout(){
|
|||||||
<>
|
<>
|
||||||
{/* Example usage of imported components, adjust as needed */}
|
{/* Example usage of imported components, adjust as needed */}
|
||||||
<HomeNav />
|
<HomeNav />
|
||||||
<Home />
|
<Home /> {/*page*/}
|
||||||
<ContactMeForm />
|
|
||||||
<Portfolio />
|
|
||||||
<Drone />
|
<Drone />
|
||||||
|
<Portfolio />
|
||||||
|
<div style={{ margin: "6em auto", marginTop: "15em", maxWidth: "80vw" }}>
|
||||||
|
<ContactMeForm />
|
||||||
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,15 @@
|
|||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Exo:ital,wght@0,100..900;1,100..900&display=swap');
|
||||||
|
|
||||||
|
:root{
|
||||||
|
--c-background: #031D44; /*background*/
|
||||||
|
--c-background-light: #04395E; /*background-highlight*/
|
||||||
|
--c-boxes: #24719f;; /*boxes*/
|
||||||
|
--c-lines: #87a9da; /*lines*/
|
||||||
|
--c-text: #CAF0F8; /*text*/
|
||||||
|
--c-other: #70A288; /*other*/
|
||||||
|
}
|
||||||
|
|
||||||
html{
|
html{
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user