Fixed chapter selection on last chapter
This commit is contained in:
@@ -3,14 +3,16 @@ import type { Component } from 'solid-js';
|
||||
import Controls from './components/Controls';
|
||||
import Hero from './components/Hero'
|
||||
import Projects from './components/Projects/Projects'
|
||||
import ContactForm from './components/ContactForm'
|
||||
|
||||
const App: Component = () => {
|
||||
return (
|
||||
<>
|
||||
<div class='App'>
|
||||
<Controls/>
|
||||
<Hero/>
|
||||
<Projects/>
|
||||
</>
|
||||
<ContactForm/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
24
src/components/ContactForm.module.css
Normal file
24
src/components/ContactForm.module.css
Normal file
@@ -0,0 +1,24 @@
|
||||
.ContactForm {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: var(--gap-md);
|
||||
margin-left: var(--gap-xl);
|
||||
margin-right: var(--gap-xl);
|
||||
}
|
||||
|
||||
.ContactForm > * {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: var(--gap-sm);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1080px) {
|
||||
.ContactForm {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
51
src/components/ContactForm.tsx
Normal file
51
src/components/ContactForm.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useI18n } from "@solid-primitives/i18n";
|
||||
import { createViewportObserver } from '@solid-primitives/intersection-observer';
|
||||
|
||||
import { useStore } from '../store/index';
|
||||
import type { Store } from '../store/index';
|
||||
import { scrollHereWhenSelected } from "../utlis/scroll";
|
||||
import styles from './ContactForm.module.css';
|
||||
|
||||
export default () => {
|
||||
const [t] = useI18n();
|
||||
const [state, setters] = useStore() as Store;
|
||||
const [observer] = createViewportObserver({threshold: 0.9});
|
||||
const chapterName = 'mail';
|
||||
|
||||
return (
|
||||
<form
|
||||
use:observer={(event) => {
|
||||
if (event.isIntersecting) {
|
||||
if (!state.scrolling()) {
|
||||
setters.setVisibleChapter(chapterName);
|
||||
} else if (state.visibleChapter() === chapterName) {
|
||||
setters.setScrolling(false)
|
||||
}
|
||||
// Сраный костыль
|
||||
} else if (state.visibleChapter() === chapterName) {
|
||||
setters.setVisibleChapter('projects');
|
||||
}
|
||||
}}
|
||||
ref={(element) => scrollHereWhenSelected(element, [state, setters], chapterName)}
|
||||
class={styles.ContactForm}
|
||||
action="https://send.pageclip.co/ipMETNW8CCV8ka21myU6D22bvnOAV0Ag"
|
||||
method="post"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder={t('name')}
|
||||
></input>
|
||||
|
||||
<textarea
|
||||
name="message"
|
||||
placeholder="Write something.."
|
||||
></textarea>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
value="Submit"
|
||||
></input>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -57,7 +57,6 @@
|
||||
transition: width .6s, height .6s, transform .6s;
|
||||
width: 0;
|
||||
height: 0;
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
|
||||
.selected {
|
||||
|
||||
@@ -9,31 +9,31 @@ import gridIcon from '../assets/icons/grid.svg'
|
||||
import mailIcon from '../assets/icons/mail.svg'
|
||||
|
||||
export default () => {
|
||||
const [store, {setVisibleChapter}] = useStore() as Store;
|
||||
const [state, {setVisibleChapter, setScrolling}] = useStore() as Store;
|
||||
const [selected, setSelected] = createSignal('home');
|
||||
const [blobby, setBlobby] = createSignal(['home']);
|
||||
const chapters = [
|
||||
{ name: 'home', icon: homeIcon },
|
||||
{ name: 'projects', icon: gridIcon },
|
||||
{ name: 'whatever', icon: mailIcon },
|
||||
{ name: 'mail', icon: mailIcon },
|
||||
]
|
||||
|
||||
const selectChapter = (chapterName: string) => {
|
||||
if (chapterName !== selected()) {
|
||||
setSelected(chapterName)
|
||||
setVisibleChapter(chapterName)
|
||||
setBlobby(b => [chapterName, ...b])
|
||||
setSelected(chapterName);
|
||||
setVisibleChapter(chapterName);
|
||||
setBlobby(b => [chapterName, ...b]);
|
||||
setTimeout(() => {
|
||||
setBlobby(b => [chapterName])
|
||||
}, 500)
|
||||
setBlobby(b => [chapterName]);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
createEffect((prev) => {
|
||||
if (prev !== store.visibleChapter()) {
|
||||
selectChapter(store.visibleChapter());
|
||||
if (prev !== state.visibleChapter()) {
|
||||
selectChapter(state.visibleChapter());
|
||||
}
|
||||
return store.visibleChapter();
|
||||
return state.visibleChapter();
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -54,6 +54,7 @@ export default () => {
|
||||
<For each={chapters}>{(chapter) =>
|
||||
<div
|
||||
onClick={() => {
|
||||
setScrolling(true);
|
||||
selectChapter(chapter.name)
|
||||
}}
|
||||
class={styles.control}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.Hero {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -23,6 +24,19 @@
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.blur {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
backdrop-filter: blur(50px);
|
||||
opacity: 1;
|
||||
transition: opacity 2.5s;
|
||||
transition-delay: .5s;
|
||||
}
|
||||
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
|
||||
@@ -9,17 +9,22 @@ import styles from './Hero.module.css';
|
||||
|
||||
export default () => {
|
||||
const [t] = useI18n();
|
||||
const [observer] = createViewportObserver({threshold: 0.9});
|
||||
const [store, { setVisibleChapter }] = useStore() as Store;
|
||||
const [observer] = createViewportObserver({threshold: 0.8});
|
||||
const [state, setters] = useStore() as Store;
|
||||
const chapterName = 'home'
|
||||
|
||||
return (
|
||||
<header
|
||||
use:observer={(event) => {
|
||||
if (event.isIntersecting) {
|
||||
setVisibleChapter('home');
|
||||
}}
|
||||
}
|
||||
ref={(element) => scrollHereWhenSelected(element, store, 'home')}
|
||||
if (!state.scrolling()) {
|
||||
setters.setVisibleChapter(chapterName);
|
||||
} else if (state.visibleChapter() === chapterName) {
|
||||
setters.setScrolling(false)
|
||||
}
|
||||
}
|
||||
}}
|
||||
ref={(element) => scrollHereWhenSelected(element, [state, setters], chapterName)}
|
||||
class={styles.Hero}
|
||||
>
|
||||
<h1 class={styles.gradientText}>
|
||||
@@ -30,6 +35,13 @@ export default () => {
|
||||
</div>
|
||||
|
||||
<Links />
|
||||
|
||||
<div
|
||||
class={styles.blur}
|
||||
ref={(element) => {
|
||||
setTimeout(() => element.style.opacity = '0')
|
||||
}}
|
||||
/>
|
||||
</header>
|
||||
)
|
||||
};
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
.Projects {
|
||||
padding: var(--gap-md);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--gap-lg);
|
||||
margin-left: 130px;
|
||||
margin-right: 130px;
|
||||
margin-left: var(--gap-xl);
|
||||
margin-right: var(--gap-xl);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1080px) {
|
||||
.Projects {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,23 @@ import Project from './Project';
|
||||
import type { Project as ProjectType } from './projectList';
|
||||
|
||||
export default () => {
|
||||
const [store, { setVisibleChapter }] = useStore() as Store;
|
||||
const [observer] = createViewportObserver({threshold: 0.5});
|
||||
const [state, setters] = useStore() as Store;
|
||||
const [observer] = createViewportObserver({threshold: 0.1});
|
||||
const chapterName = 'projects'
|
||||
|
||||
return (
|
||||
<div
|
||||
use:observer={(event) => {
|
||||
if (event.isIntersecting) {
|
||||
setVisibleChapter('projects');
|
||||
}}
|
||||
}
|
||||
ref={(element) => scrollHereWhenSelected(element, store, 'projects')}
|
||||
console.log(chapterName);
|
||||
if (!state.scrolling()) {
|
||||
setters.setVisibleChapter(chapterName);
|
||||
} else if (state.visibleChapter() === chapterName) {
|
||||
setters.setScrolling(false)
|
||||
}
|
||||
}
|
||||
}}
|
||||
ref={(element) => scrollHereWhenSelected(element, [state, setters], chapterName)}
|
||||
class={styles.Projects}
|
||||
>
|
||||
<For each={projects}>{(project: ProjectType) =>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
--gap-sm: 18px;
|
||||
--gap-md: 36px;
|
||||
--gap-lg: 72px;
|
||||
--gap-xl: 144px;
|
||||
|
||||
--radius-md: 12px;
|
||||
|
||||
@@ -19,6 +20,10 @@
|
||||
--font-md: normal normal 400 18px var(--ff-default);
|
||||
}
|
||||
|
||||
.App {
|
||||
padding-bottom: var(--gap-xl);
|
||||
}
|
||||
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
@@ -41,3 +46,12 @@ code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
background: var(--clr-bg-secondary);
|
||||
border: none;
|
||||
padding: var(--gap-sm);
|
||||
border-radius: var(--radius-md);
|
||||
display: inline-block;
|
||||
color: var(--clr-text);
|
||||
}
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
"warframe_desc": "A service that monitors prices of items on warframe.market and calculates profitable gaps between them.",
|
||||
"worktime_desc": "A PWA that I use daily to track my time spent working. Full offline support.",
|
||||
"studybuddy_desc": "A PWA for splitting into groups or taking topics for an assignment.",
|
||||
"vkmute_desc": "A Browser extension that allows you to mute people in group chats on VK.com."
|
||||
"vkmute_desc": "A Browser extension that allows you to mute people in group chats on VK.com.",
|
||||
"name": "Your name"
|
||||
}
|
||||
|
||||
@@ -2,20 +2,30 @@ import { createSignal, createContext, useContext } from "solid-js";
|
||||
import type { Accessor, Setter } from 'solid-js';
|
||||
|
||||
export type Store = [
|
||||
{ visibleChapter: Accessor<string> },
|
||||
{ setVisibleChapter: Setter<string> }
|
||||
{
|
||||
visibleChapter: Accessor<string>,
|
||||
scrolling: Accessor<boolean>,
|
||||
},
|
||||
{
|
||||
setVisibleChapter: Setter<string>,
|
||||
setScrolling: Setter<boolean>,
|
||||
}
|
||||
]
|
||||
|
||||
const StoreContext = createContext<Store>();
|
||||
|
||||
export function StoreProvider(props: any) {
|
||||
const [visibleChapter, setVisibleChapter] = createSignal('home');
|
||||
const [scrolling, setScrolling] = createSignal(false);
|
||||
|
||||
const store: Store = [
|
||||
{
|
||||
visibleChapter
|
||||
visibleChapter,
|
||||
scrolling,
|
||||
},
|
||||
{
|
||||
setVisibleChapter
|
||||
setVisibleChapter,
|
||||
setScrolling
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { createEffect } from "solid-js";
|
||||
|
||||
export const scrollHereWhenSelected = (element: HTMLElement, store, chapter) => {
|
||||
import type { Store } from '../store/index';
|
||||
|
||||
export const scrollHereWhenSelected = (element: HTMLElement, store: Store, chapter: string) => {
|
||||
const [state, setters] = store;
|
||||
return createEffect((prev) => {
|
||||
if (prev !== store.visibleChapter() && store.visibleChapter() === chapter) {
|
||||
element.scrollIntoView({behavior: "smooth"})
|
||||
if (prev !== state.visibleChapter() && state.visibleChapter() === chapter && state.scrolling()) {
|
||||
element.scrollIntoView({behavior: "smooth"});
|
||||
setters.setScrolling(true);
|
||||
}
|
||||
return store.visibleChapter();
|
||||
return state.visibleChapter();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user