Fixed chapter selection on last chapter

This commit is contained in:
2022-05-21 15:55:10 +03:00
parent 9770de6733
commit 448cb46e9e
13 changed files with 184 additions and 37 deletions

View File

@@ -3,14 +3,16 @@ import type { Component } from 'solid-js';
import Controls from './components/Controls'; import Controls from './components/Controls';
import Hero from './components/Hero' import Hero from './components/Hero'
import Projects from './components/Projects/Projects' import Projects from './components/Projects/Projects'
import ContactForm from './components/ContactForm'
const App: Component = () => { const App: Component = () => {
return ( return (
<> <div class='App'>
<Controls/> <Controls/>
<Hero/> <Hero/>
<Projects/> <Projects/>
</> <ContactForm/>
</div>
); );
}; };

View 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;
}
}

View 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>
)
}

View File

@@ -57,7 +57,6 @@
transition: width .6s, height .6s, transform .6s; transition: width .6s, height .6s, transform .6s;
width: 0; width: 0;
height: 0; height: 0;
transform: translate(-50%, 50%);
} }
.selected { .selected {

View File

@@ -9,31 +9,31 @@ import gridIcon from '../assets/icons/grid.svg'
import mailIcon from '../assets/icons/mail.svg' import mailIcon from '../assets/icons/mail.svg'
export default () => { export default () => {
const [store, {setVisibleChapter}] = useStore() as Store; const [state, {setVisibleChapter, setScrolling}] = useStore() as Store;
const [selected, setSelected] = createSignal('home'); const [selected, setSelected] = createSignal('home');
const [blobby, setBlobby] = createSignal(['home']); const [blobby, setBlobby] = createSignal(['home']);
const chapters = [ const chapters = [
{ name: 'home', icon: homeIcon }, { name: 'home', icon: homeIcon },
{ name: 'projects', icon: gridIcon }, { name: 'projects', icon: gridIcon },
{ name: 'whatever', icon: mailIcon }, { name: 'mail', icon: mailIcon },
] ]
const selectChapter = (chapterName: string) => { const selectChapter = (chapterName: string) => {
if (chapterName !== selected()) { if (chapterName !== selected()) {
setSelected(chapterName) setSelected(chapterName);
setVisibleChapter(chapterName) setVisibleChapter(chapterName);
setBlobby(b => [chapterName, ...b]) setBlobby(b => [chapterName, ...b]);
setTimeout(() => { setTimeout(() => {
setBlobby(b => [chapterName]) setBlobby(b => [chapterName]);
}, 500) }, 500);
} }
} }
createEffect((prev) => { createEffect((prev) => {
if (prev !== store.visibleChapter()) { if (prev !== state.visibleChapter()) {
selectChapter(store.visibleChapter()); selectChapter(state.visibleChapter());
} }
return store.visibleChapter(); return state.visibleChapter();
}); });
return ( return (
@@ -54,6 +54,7 @@ export default () => {
<For each={chapters}>{(chapter) => <For each={chapters}>{(chapter) =>
<div <div
onClick={() => { onClick={() => {
setScrolling(true);
selectChapter(chapter.name) selectChapter(chapter.name)
}} }}
class={styles.control} class={styles.control}

View File

@@ -1,4 +1,5 @@
.Hero { .Hero {
position: relative;
height: 100vh; height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -23,6 +24,19 @@
word-break: break-word; 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 { @keyframes gradient {
0% { 0% {
background-position: 0% 50%; background-position: 0% 50%;

View File

@@ -9,17 +9,22 @@ import styles from './Hero.module.css';
export default () => { export default () => {
const [t] = useI18n(); const [t] = useI18n();
const [observer] = createViewportObserver({threshold: 0.9}); const [observer] = createViewportObserver({threshold: 0.8});
const [store, { setVisibleChapter }] = useStore() as Store; const [state, setters] = useStore() as Store;
const chapterName = 'home'
return ( return (
<header <header
use:observer={(event) => { use:observer={(event) => {
if (event.isIntersecting) { if (event.isIntersecting) {
setVisibleChapter('home'); if (!state.scrolling()) {
}} setters.setVisibleChapter(chapterName);
} else if (state.visibleChapter() === chapterName) {
setters.setScrolling(false)
} }
ref={(element) => scrollHereWhenSelected(element, store, 'home')} }
}}
ref={(element) => scrollHereWhenSelected(element, [state, setters], chapterName)}
class={styles.Hero} class={styles.Hero}
> >
<h1 class={styles.gradientText}> <h1 class={styles.gradientText}>
@@ -30,6 +35,13 @@ export default () => {
</div> </div>
<Links /> <Links />
<div
class={styles.blur}
ref={(element) => {
setTimeout(() => element.style.opacity = '0')
}}
/>
</header> </header>
) )
}; };

View File

@@ -1,10 +1,19 @@
.Projects { .Projects {
padding: var(--gap-md); padding: var(--gap-md);
display: flex; display: flex;
justify-content: center; justify-content: space-between;
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: var(--gap-lg); gap: var(--gap-lg);
margin-left: 130px; margin-left: var(--gap-xl);
margin-right: 130px; margin-right: var(--gap-xl);
}
@media screen and (max-width: 1080px) {
.Projects {
margin: 0;
width: 100%;
box-sizing: border-box;
flex-direction: column;
}
} }

View File

@@ -10,17 +10,23 @@ import Project from './Project';
import type { Project as ProjectType } from './projectList'; import type { Project as ProjectType } from './projectList';
export default () => { export default () => {
const [store, { setVisibleChapter }] = useStore() as Store; const [state, setters] = useStore() as Store;
const [observer] = createViewportObserver({threshold: 0.5}); const [observer] = createViewportObserver({threshold: 0.1});
const chapterName = 'projects'
return ( return (
<div <div
use:observer={(event) => { use:observer={(event) => {
if (event.isIntersecting) { if (event.isIntersecting) {
setVisibleChapter('projects'); console.log(chapterName);
}} if (!state.scrolling()) {
setters.setVisibleChapter(chapterName);
} else if (state.visibleChapter() === chapterName) {
setters.setScrolling(false)
} }
ref={(element) => scrollHereWhenSelected(element, store, 'projects')} }
}}
ref={(element) => scrollHereWhenSelected(element, [state, setters], chapterName)}
class={styles.Projects} class={styles.Projects}
> >
<For each={projects}>{(project: ProjectType) => <For each={projects}>{(project: ProjectType) =>

View File

@@ -9,6 +9,7 @@
--gap-sm: 18px; --gap-sm: 18px;
--gap-md: 36px; --gap-md: 36px;
--gap-lg: 72px; --gap-lg: 72px;
--gap-xl: 144px;
--radius-md: 12px; --radius-md: 12px;
@@ -19,6 +20,10 @@
--font-md: normal normal 400 18px var(--ff-default); --font-md: normal normal 400 18px var(--ff-default);
} }
.App {
padding-bottom: var(--gap-xl);
}
body { body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
@@ -41,3 +46,12 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; 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);
}

View File

@@ -7,5 +7,6 @@
"warframe_desc": "A service that monitors prices of items on warframe.market and calculates profitable gaps between them.", "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.", "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.", "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"
} }

View File

@@ -2,20 +2,30 @@ import { createSignal, createContext, useContext } from "solid-js";
import type { Accessor, Setter } from 'solid-js'; import type { Accessor, Setter } from 'solid-js';
export type Store = [ 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>(); const StoreContext = createContext<Store>();
export function StoreProvider(props: any) { export function StoreProvider(props: any) {
const [visibleChapter, setVisibleChapter] = createSignal('home'); const [visibleChapter, setVisibleChapter] = createSignal('home');
const [scrolling, setScrolling] = createSignal(false);
const store: Store = [ const store: Store = [
{ {
visibleChapter visibleChapter,
scrolling,
}, },
{ {
setVisibleChapter setVisibleChapter,
setScrolling
} }
]; ];

View File

@@ -1,10 +1,14 @@
import { createEffect } from "solid-js"; 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) => { return createEffect((prev) => {
if (prev !== store.visibleChapter() && store.visibleChapter() === chapter) { if (prev !== state.visibleChapter() && state.visibleChapter() === chapter && state.scrolling()) {
element.scrollIntoView({behavior: "smooth"}) element.scrollIntoView({behavior: "smooth"});
setters.setScrolling(true);
} }
return store.visibleChapter(); return state.visibleChapter();
}); });
} }