diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/Datetime.tsx | 28 | ||||
| -rw-r--r-- | src/components/Geshem.tsx | 65 | ||||
| -rw-r--r-- | src/components/Map.tsx | 105 | ||||
| -rw-r--r-- | src/components/Slider.tsx | 86 |
4 files changed, 0 insertions, 284 deletions
diff --git a/src/components/Datetime.tsx b/src/components/Datetime.tsx deleted file mode 100644 index 8b66a4f..0000000 --- a/src/components/Datetime.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; -import { DateTime as LuxonDateTime } from "luxon"; - -interface DateTimeProps { - images: string[]; - slider: number; -} - -export function DateTime({ images, slider }: DateTimeProps) { - let datetime: LuxonDateTime | null = null; - - if (images.length > 0 && images[slider]) { - const ds = images[slider].substring(5, 18); - datetime = LuxonDateTime.fromFormat(ds, "yyyyMMdd/HHmm", { - zone: "utc", - }).setZone("Asia/Jerusalem"); - } - - const date = datetime ? datetime.toFormat("dd/MM/y") : ""; - const time = datetime ? datetime.toFormat("HH:mm") : ""; - - return ( - <div id="datetime" className="absolute top-5 left-5 text-white font-mono"> - <div id="date" className="text-xl">{date}</div> - <div id="time" className="text-4xl">{time}</div> - </div> - ); -}
\ No newline at end of file diff --git a/src/components/Geshem.tsx b/src/components/Geshem.tsx deleted file mode 100644 index 982d841..0000000 --- a/src/components/Geshem.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useState, useEffect } from "react"; - -import { Map } from "./Map"; -import { Slider } from "./Slider"; -import { DateTime } from "./Datetime"; - -import { IMAGES_BASE_URL, PLAYBACK_HOURS, PLAYBACK_SLOTS } from "../config"; - -interface GeshemProps { - date?: string; -} - -export function Geshem({ date }: GeshemProps) { - const [images, setImages] = useState<string[]>([]); - const [playback] = useState( - date || - (typeof window !== 'undefined' ? new URL(window.location.toString()).searchParams.get("history") : null) || - undefined - ); - const [slider, setSlider] = useState(playback ? PLAYBACK_SLOTS : 9); - - useEffect(() => { - const fetchImages = async () => - fetch(`${IMAGES_BASE_URL}/imgs.json`) - .then(res => res.json()) - .then(imgs => (imgs as any)["280"]) - .then(setImages); - - const buildPlayback = async () => { - const date = playback; - const hours = Array.from(Array(PLAYBACK_HOURS).keys()).map( - h => `${String(h).padStart(2, "0")}` - ); - const minutes = Array.from(Array(6).keys()).map( - m => `${String(m * 10).padStart(2, "0")}` - ); - const paths = hours.reduce<string[]>( - (acc, h) => - acc.concat(minutes.map(m => `imgs/${date}/${h}${m}/280.png`)), - [] - ); - setImages(paths); - }; - - let timer: number; - if (playback) { - buildPlayback(); - } else { - fetchImages(); - timer = window.setInterval(fetchImages, 60 * 1000); - } - - return () => { - if (timer !== undefined) clearInterval(timer); - }; - }, [playback]); - - return ( - <> - <Map images={images} slider={slider} /> - <DateTime images={images} slider={slider} /> - <Slider slider={slider} playback={playback} setSlider={setSlider} /> - </> - ); -}
\ No newline at end of file diff --git a/src/components/Map.tsx b/src/components/Map.tsx deleted file mode 100644 index 070dc5c..0000000 --- a/src/components/Map.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import React, { useState, useEffect, useRef } from "react"; -import mapboxgl from 'mapbox-gl'; - -import { - MAPBOX_ACCESS_TOKEN, - IMAGE_COORDINATES, - IMAGES_BASE_URL -} from "../config"; - -import "mapbox-gl/dist/mapbox-gl.css"; - -interface MapProps { - slider: number; - images: string[]; -} - -export function Map({ slider, images }: MapProps) { - const mapContainer = useRef<HTMLDivElement>(null); - const map = useRef<mapboxgl.Map | null>(null); - - const [lng] = useState(35); - const [lat] = useState(31.9); - const [zoom] = useState(6.3); - const [loaded, setLoaded] = useState(false); - - const prevImages = useRef(images).current; - - useEffect(() => { - if (map.current || !mapContainer.current) return; - - map.current = new mapboxgl.Map({ - accessToken: MAPBOX_ACCESS_TOKEN, - container: mapContainer.current, - style: "mapbox://styles/mapbox/dark-v9", - center: [lng, lat], - zoom: zoom, - minZoom: 5, - maxZoom: 10, - hash: false, - }); - - map.current.on("style.load", () => { - setLoaded(true); - }); - }, [lng, lat, zoom]); - - useEffect(() => { - if (!loaded || !map.current) return; - - // remove old layers - prevImages.forEach((img) => { - if (!images.includes(img)) { - if (map.current?.getLayer(`layer-${img}`)) { - map.current.removeLayer(`layer-${img}`); - } - if (map.current?.getSource(`source-${img}`)) { - map.current.removeSource(`source-${img}`); - } - } - }); - - // add new layers - images.forEach((img, i) => { - if (!prevImages.includes(img) && !map.current?.getSource(`source-${img}`)) { - map.current?.addSource(`source-${img}`, { - type: "image", - url: `${IMAGES_BASE_URL}/${img}`, - coordinates: IMAGE_COORDINATES - }); - map.current?.addLayer({ - id: `layer-${img}`, - source: `source-${img}`, - type: "raster", - paint: { - "raster-opacity": 0, - "raster-opacity-transition": { - duration: 0 - } - } - }); - } - }); - }, [loaded, prevImages, images]); - - useEffect(() => { - if (!loaded || !images.length || !map.current) return; - - if (images[slider]) { - map.current.setPaintProperty(`layer-${images[slider]}`, "raster-opacity", 0.85); - } - - return () => { - // callback will hide the previous layer with the previous slider value - if (images[slider] && map.current?.getLayer(`layer-${images[slider]}`)) { - map.current.setPaintProperty(`layer-${images[slider]}`, "raster-opacity", 0); - } - }; - }, [loaded, slider, images]); - - return ( - <div> - <div ref={mapContainer} className="map-container h-screen w-screen" /> - </div> - ); -}
\ No newline at end of file diff --git a/src/components/Slider.tsx b/src/components/Slider.tsx deleted file mode 100644 index 22a8f8f..0000000 --- a/src/components/Slider.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useCallback, useRef, useState } from "react"; -import { PLAYBACK_SLOTS } from "../config"; - -interface SliderProps { - playback?: string; - slider: number; - setSlider: React.Dispatch<React.SetStateAction<number>>; -} - -export function Slider({ playback, slider, setSlider }: SliderProps) { - const sliderRef = useRef<HTMLDivElement>(null); - const [isDragging, setIsDragging] = useState(false); - - const max = playback ? PLAYBACK_SLOTS : 9; - - const handleStart = useCallback((e: React.MouseEvent | React.TouchEvent) => { - setIsDragging(true); - updateSliderValue(e); - }, []); - - const updateSliderValue = useCallback((e: MouseEvent | React.MouseEvent | TouchEvent | React.TouchEvent) => { - if (!sliderRef.current) return; - - const rect = sliderRef.current.getBoundingClientRect(); - let clientX: number; - - if ('touches' in e) { - clientX = e.touches?.[0]?.clientX || (e as TouchEvent).changedTouches?.[0]?.clientX || 0; - } else { - clientX = e.clientX; - } - - const percentage = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)); - const value = Math.round(percentage * max); - setSlider(value); - }, [max, setSlider]); - - const handleMove = useCallback((e: MouseEvent | TouchEvent) => { - if (isDragging) { - e.preventDefault(); - updateSliderValue(e); - } - }, [isDragging, updateSliderValue]); - - const handleEnd = useCallback(() => { - setIsDragging(false); - }, []); - - React.useEffect(() => { - if (isDragging) { - document.addEventListener('mousemove', handleMove); - document.addEventListener('mouseup', handleEnd); - document.addEventListener('touchmove', handleMove, { passive: false }); - document.addEventListener('touchend', handleEnd); - - return () => { - document.removeEventListener('mousemove', handleMove); - document.removeEventListener('mouseup', handleEnd); - document.removeEventListener('touchmove', handleMove); - document.removeEventListener('touchend', handleEnd); - }; - } - }, [isDragging, handleMove, handleEnd]); - - const percentage = (slider / max) * 100; - - return ( - <div id="slider" className="py-2.5 fixed bottom-[8vh] w-[30vw] ml-[35vw] max-[812px]:w-[80vw] max-[812px]:ml-[8vw] max-[812px]:z-10"> - <div - ref={sliderRef} - className="relative h-5 bg-blue-500 rounded-full cursor-pointer touch-none" - onMouseDown={handleStart} - onTouchStart={handleStart} - > - <div - className={`absolute -top-2.5 w-10 h-10 bg-white rounded-full shadow-md z-[1] ${ - isDragging ? 'cursor-grabbing' : 'cursor-grab' - }`} - style={{ - left: `calc(${percentage}% - 20px)` - }} - /> - </div> - </div> - ); -}
\ No newline at end of file |
