mirror of
https://github.com/Sped0n/bridget.git
synced 2026-04-21 13:39:30 -07:00
refactor: reduce amount of createEffect and improve imports (#284)
* refactor: update import syntax * feat: add GalleryImage component for simplicity * refactor: replace createEffect in GalleryNav with createMemo * refactor: refactor Gallery component logic and improve imports
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { createSignal, onCleanup, onMount, type Accessor, type JSX } from 'solid-js'
|
import { createSignal, onCleanup, onMount, type Accessor, type JSX } from 'solid-js'
|
||||||
|
|
||||||
export function CustomCursor(props: {
|
export default function CustomCursor(props: {
|
||||||
children?: JSX.Element
|
children?: JSX.Element
|
||||||
active: Accessor<boolean>
|
active: Accessor<boolean>
|
||||||
cursorText: Accessor<string>
|
cursorText: Accessor<string>
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { Show, createMemo, createSignal, type JSX } from 'solid-js'
|
|||||||
import type { ImageJSON } from '../resources'
|
import type { ImageJSON } from '../resources'
|
||||||
import type { Vector } from '../utils'
|
import type { Vector } from '../utils'
|
||||||
|
|
||||||
import { CustomCursor } from './customCursor'
|
import CustomCursor from './customCursor'
|
||||||
import { Nav } from './nav'
|
import Nav from './nav'
|
||||||
import { Stage } from './stage'
|
import Stage from './stage'
|
||||||
import { StageNav } from './stageNav'
|
import StageNav from './stageNav'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* interfaces and types
|
* interfaces and types
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ function updateIndexText(indexValue: string, indexLength: string): void {
|
|||||||
* Nav component
|
* Nav component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function Nav(): null {
|
export default function Nav(): null {
|
||||||
const [state, { incThreshold, decThreshold }] = useState()
|
const [state, { incThreshold, decThreshold }] = useState()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ function onMutation<T extends HTMLElement>(
|
|||||||
* Stage component
|
* Stage component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function Stage(props: {
|
export default function Stage(props: {
|
||||||
ijs: ImageJSON[]
|
ijs: ImageJSON[]
|
||||||
setIsLoading: Setter<boolean>
|
setIsLoading: Setter<boolean>
|
||||||
isOpen: Accessor<boolean>
|
isOpen: Accessor<boolean>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { decrement, increment, type Vector } from '../utils'
|
|||||||
|
|
||||||
import type { HistoryItem } from './layout'
|
import type { HistoryItem } from './layout'
|
||||||
|
|
||||||
export function StageNav(props: {
|
export default function StageNav(props: {
|
||||||
children?: JSX.Element
|
children?: JSX.Element
|
||||||
prevText: string
|
prevText: string
|
||||||
closeText: string
|
closeText: string
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ function onIntersection<T extends HTMLElement>(
|
|||||||
}).observe(element)
|
}).observe(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Collection(props: {
|
export default function Collection(props: {
|
||||||
children?: JSX.Element
|
children?: JSX.Element
|
||||||
ijs: ImageJSON[]
|
ijs: ImageJSON[]
|
||||||
isAnimating: Accessor<boolean>
|
isAnimating: Accessor<boolean>
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import { type gsap } from 'gsap'
|
import { type gsap } from 'gsap'
|
||||||
import {
|
import {
|
||||||
createEffect,
|
createEffect,
|
||||||
|
createSignal,
|
||||||
For,
|
For,
|
||||||
on,
|
on,
|
||||||
onMount,
|
onMount,
|
||||||
|
Show,
|
||||||
type Accessor,
|
type Accessor,
|
||||||
type JSX,
|
type JSX,
|
||||||
type Setter
|
type Setter
|
||||||
} from 'solid-js'
|
} from 'solid-js'
|
||||||
|
import { createStore } from 'solid-js/store'
|
||||||
import { type Swiper } from 'swiper'
|
import { type Swiper } from 'swiper'
|
||||||
import invariant from 'tiny-invariant'
|
import invariant from 'tiny-invariant'
|
||||||
|
|
||||||
@@ -15,8 +18,8 @@ import { type ImageJSON } from '../resources'
|
|||||||
import { useState } from '../state'
|
import { useState } from '../state'
|
||||||
import { loadGsap, type Vector } from '../utils'
|
import { loadGsap, type Vector } from '../utils'
|
||||||
|
|
||||||
import { capitalizeFirstLetter, GalleryNav } from './galleryNav'
|
import GalleryImage from './galleryImage'
|
||||||
import type { MobileImage } from './layout'
|
import GalleryNav, { capitalizeFirstLetter } from './galleryNav'
|
||||||
|
|
||||||
function removeDuplicates<T>(arr: T[]): T[] {
|
function removeDuplicates<T>(arr: T[]): T[] {
|
||||||
if (arr.length < 2) return arr // optimization
|
if (arr.length < 2) return arr // optimization
|
||||||
@@ -28,7 +31,7 @@ async function loadSwiper(): Promise<typeof Swiper> {
|
|||||||
return s.Swiper
|
return s.Swiper
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Gallery(props: {
|
export default function Gallery(props: {
|
||||||
children?: JSX.Element
|
children?: JSX.Element
|
||||||
ijs: ImageJSON[]
|
ijs: ImageJSON[]
|
||||||
closeText: string
|
closeText: string
|
||||||
@@ -43,10 +46,6 @@ export function Gallery(props: {
|
|||||||
let _gsap: typeof gsap
|
let _gsap: typeof gsap
|
||||||
let _swiper: Swiper
|
let _swiper: Swiper
|
||||||
|
|
||||||
// eslint-disable-next-line solid/reactivity
|
|
||||||
const imgs: MobileImage[] = Array<MobileImage>(props.ijs.length)
|
|
||||||
// eslint-disable-next-line solid/reactivity
|
|
||||||
const loadingDivs: HTMLDivElement[] = Array<HTMLDivElement>(props.ijs.length)
|
|
||||||
let curtain: HTMLDivElement | undefined
|
let curtain: HTMLDivElement | undefined
|
||||||
let gallery: HTMLDivElement | undefined
|
let gallery: HTMLDivElement | undefined
|
||||||
let galleryInner: HTMLDivElement | undefined
|
let galleryInner: HTMLDivElement | undefined
|
||||||
@@ -56,16 +55,18 @@ export function Gallery(props: {
|
|||||||
|
|
||||||
// states
|
// states
|
||||||
let lastIndex = -1
|
let lastIndex = -1
|
||||||
let libLoaded = false
|
|
||||||
let mounted = false
|
let mounted = false
|
||||||
let navigateVector: Vector = 'none'
|
let navigateVector: Vector = 'none'
|
||||||
|
|
||||||
const [state, { setIndex }] = useState()
|
const [state, { setIndex }] = useState()
|
||||||
|
const [libLoaded, setLibLoaded] = createSignal(false)
|
||||||
|
// eslint-disable-next-line solid/reactivity
|
||||||
|
const [loads, setLoads] = createStore(Array<boolean>(props.ijs.length).fill(false))
|
||||||
|
|
||||||
// helper functions
|
// helper functions
|
||||||
const slideUp: () => void = () => {
|
const slideUp: () => void = () => {
|
||||||
// isAnimating is prechecked in isOpen effect
|
// isAnimating is prechecked in isOpen effect
|
||||||
if (!libLoaded || !mounted) return
|
if (!libLoaded() || !mounted) return
|
||||||
props.setIsAnimating(true)
|
props.setIsAnimating(true)
|
||||||
|
|
||||||
invariant(curtain, 'curtain is not defined')
|
invariant(curtain, 'curtain is not defined')
|
||||||
@@ -133,11 +134,7 @@ export function Gallery(props: {
|
|||||||
activeImagesIndex = [currentIndex, nextIndex, prevIndex]
|
activeImagesIndex = [currentIndex, nextIndex, prevIndex]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
removeDuplicates(activeImagesIndex).forEach((i) => {
|
setLoads(removeDuplicates(activeImagesIndex), true)
|
||||||
const e = imgs[i]
|
|
||||||
if (e.src === e.dataset.src) return // already loaded
|
|
||||||
e.src = e.dataset.src
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeSlide: (slide: number) => void = (slide) => {
|
const changeSlide: (slide: number) => void = (slide) => {
|
||||||
@@ -149,22 +146,6 @@ export function Gallery(props: {
|
|||||||
|
|
||||||
// effects
|
// effects
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
imgs.forEach((img, i) => {
|
|
||||||
const loadingDiv = loadingDivs[i]
|
|
||||||
img.addEventListener(
|
|
||||||
'load',
|
|
||||||
() => {
|
|
||||||
if (state().index !== parseInt(img.dataset.index)) {
|
|
||||||
_gsap.set(img, { opacity: 1 })
|
|
||||||
_gsap.set(loadingDiv, { opacity: 0 })
|
|
||||||
} else {
|
|
||||||
_gsap.to(img, { opacity: 1, delay: 0.5, duration: 0.5, ease: 'power3.out' })
|
|
||||||
_gsap.to(loadingDiv, { opacity: 0, duration: 0.5, ease: 'power3.in' })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ once: true, passive: true }
|
|
||||||
)
|
|
||||||
})
|
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
'touchstart',
|
'touchstart',
|
||||||
() => {
|
() => {
|
||||||
@@ -186,7 +167,7 @@ export function Gallery(props: {
|
|||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
})
|
})
|
||||||
libLoaded = true
|
setLibLoaded(true)
|
||||||
},
|
},
|
||||||
{ once: true, passive: true }
|
{ once: true, passive: true }
|
||||||
)
|
)
|
||||||
@@ -234,26 +215,19 @@ export function Gallery(props: {
|
|||||||
<div ref={gallery} class="gallery">
|
<div ref={gallery} class="gallery">
|
||||||
<div ref={galleryInner} class="galleryInner">
|
<div ref={galleryInner} class="galleryInner">
|
||||||
<div class="swiper-wrapper">
|
<div class="swiper-wrapper">
|
||||||
<For each={props.ijs}>
|
<Show when={libLoaded()}>
|
||||||
{(ij, i) => (
|
<For each={props.ijs}>
|
||||||
<div class="swiper-slide">
|
{(ij, i) => (
|
||||||
<div class="slideContainer">
|
<div class="swiper-slide">
|
||||||
<img
|
<GalleryImage
|
||||||
ref={imgs[i()]}
|
load={loads[i()]}
|
||||||
height={ij.hiImgH}
|
ij={ij}
|
||||||
width={ij.hiImgW}
|
loadingText={_loadingText}
|
||||||
data-src={ij.hiUrl}
|
|
||||||
data-index={ij.index}
|
|
||||||
alt={ij.alt}
|
|
||||||
style={{ opacity: 0 }}
|
|
||||||
/>
|
/>
|
||||||
<div ref={loadingDivs[i()]} class="loadingText">
|
|
||||||
{_loadingText}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</For>
|
||||||
</For>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<GalleryNav
|
<GalleryNav
|
||||||
|
|||||||
69
assets/ts/mobile/galleryImage.tsx
Normal file
69
assets/ts/mobile/galleryImage.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { onMount, type JSX } from 'solid-js'
|
||||||
|
import invariant from 'tiny-invariant'
|
||||||
|
|
||||||
|
import type { ImageJSON } from '../resources'
|
||||||
|
import { useState } from '../state'
|
||||||
|
import { loadGsap } from '../utils'
|
||||||
|
|
||||||
|
export default function GalleryImage(props: {
|
||||||
|
children?: JSX.Element
|
||||||
|
load: boolean
|
||||||
|
ij: ImageJSON
|
||||||
|
loadingText: string
|
||||||
|
}): JSX.Element {
|
||||||
|
let img: HTMLImageElement | undefined
|
||||||
|
let loadingDiv: HTMLDivElement | undefined
|
||||||
|
|
||||||
|
let _gsap: typeof gsap
|
||||||
|
|
||||||
|
const [state] = useState()
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loadGsap()
|
||||||
|
.then((g) => {
|
||||||
|
_gsap = g
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
img?.addEventListener(
|
||||||
|
'load',
|
||||||
|
() => {
|
||||||
|
invariant(img, 'ref must be defined')
|
||||||
|
invariant(loadingDiv, 'loadingDiv must be defined')
|
||||||
|
if (state().index !== props.ij.index) {
|
||||||
|
_gsap.set(img, { opacity: 1 })
|
||||||
|
_gsap.set(loadingDiv, { opacity: 0 })
|
||||||
|
} else {
|
||||||
|
_gsap.to(img, {
|
||||||
|
opacity: 1,
|
||||||
|
delay: 0.5,
|
||||||
|
duration: 0.5,
|
||||||
|
ease: 'power3.out'
|
||||||
|
})
|
||||||
|
_gsap.to(loadingDiv, { opacity: 0, duration: 0.5, ease: 'power3.in' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true, passive: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div class="slideContainer">
|
||||||
|
<img
|
||||||
|
ref={img}
|
||||||
|
{...(props.load && { src: props.ij.hiUrl })}
|
||||||
|
height={props.ij.hiImgH}
|
||||||
|
width={props.ij.hiImgW}
|
||||||
|
data-src={props.ij.hiUrl}
|
||||||
|
alt={props.ij.alt}
|
||||||
|
style={{ opacity: 0 }}
|
||||||
|
/>
|
||||||
|
<div ref={loadingDiv} class="loadingText">
|
||||||
|
{props.loadingText}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createEffect, on, type Accessor, type JSX, type Setter } from 'solid-js'
|
import { createMemo, type Accessor, type JSX, type Setter } from 'solid-js'
|
||||||
|
|
||||||
import { useState } from '../state'
|
import { useState } from '../state'
|
||||||
import { expand } from '../utils'
|
import { expand } from '../utils'
|
||||||
@@ -7,63 +7,35 @@ export function capitalizeFirstLetter(str: string): string {
|
|||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GalleryNav(props: {
|
export default function GalleryNav(props: {
|
||||||
children?: JSX.Element
|
children?: JSX.Element
|
||||||
closeText: string
|
closeText: string
|
||||||
isAnimating: Accessor<boolean>
|
isAnimating: Accessor<boolean>
|
||||||
setIsOpen: Setter<boolean>
|
setIsOpen: Setter<boolean>
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
// variables
|
|
||||||
const indexNums: HTMLSpanElement[] = Array<HTMLSpanElement>(8)
|
|
||||||
|
|
||||||
// states
|
// states
|
||||||
const [state] = useState()
|
const [state] = useState()
|
||||||
const stateLength = state().length
|
const indexValue = createMemo(() => expand(state().index + 1))
|
||||||
|
const indexLength = createMemo(() => expand(state().length))
|
||||||
// helper functions
|
|
||||||
const updateIndexText: () => void = () => {
|
|
||||||
const indexValue: string = expand(state().index + 1)
|
|
||||||
const indexLength: string = expand(stateLength)
|
|
||||||
indexNums.forEach((e: HTMLSpanElement, i: number) => {
|
|
||||||
if (i < 4) {
|
|
||||||
e.innerText = indexValue[i]
|
|
||||||
} else {
|
|
||||||
e.innerText = indexLength[i - 4]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClick: () => void = () => {
|
const onClick: () => void = () => {
|
||||||
if (props.isAnimating()) return
|
if (props.isAnimating()) return
|
||||||
props.setIsOpen(false)
|
props.setIsOpen(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// effects
|
|
||||||
createEffect(
|
|
||||||
on(
|
|
||||||
() => {
|
|
||||||
state()
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
updateIndexText()
|
|
||||||
},
|
|
||||||
{ defer: true }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class="nav">
|
<div class="nav">
|
||||||
<div>
|
<div>
|
||||||
<span ref={indexNums[0]} class="num" />
|
<span class="num">{indexValue()[0]}</span>
|
||||||
<span ref={indexNums[1]} class="num" />
|
<span class="num">{indexValue()[1]}</span>
|
||||||
<span ref={indexNums[2]} class="num" />
|
<span class="num">{indexValue()[2]}</span>
|
||||||
<span ref={indexNums[3]} class="num" />
|
<span class="num">{indexValue()[3]}</span>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
<span ref={indexNums[4]} class="num" />
|
<span class="num">{indexLength()[0]}</span>
|
||||||
<span ref={indexNums[5]} class="num" />
|
<span class="num">{indexLength()[1]}</span>
|
||||||
<span ref={indexNums[6]} class="num" />
|
<span class="num">{indexLength()[2]}</span>
|
||||||
<span ref={indexNums[7]} class="num" />
|
<span class="num">{indexLength()[3]}</span>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={onClick} onKeyDown={onClick}>
|
<div onClick={onClick} onKeyDown={onClick}>
|
||||||
{capitalizeFirstLetter(props.closeText)}
|
{capitalizeFirstLetter(props.closeText)}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Show, createSignal, type JSX, type Setter } from 'solid-js'
|
|||||||
|
|
||||||
import type { ImageJSON } from '../resources'
|
import type { ImageJSON } from '../resources'
|
||||||
|
|
||||||
import { Collection } from './collection'
|
import Collection from './collection'
|
||||||
import { Gallery } from './gallery'
|
import Gallery from './gallery'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* interfaces
|
* interfaces
|
||||||
|
|||||||
Reference in New Issue
Block a user