feat(map): add pitch and bearing constants for map initialization
feat(district-popup): enhance badge hover styles for crime levels feat(map): implement year timeline control with smooth animation feat(ui): create a reusable slider component using Radix UI chore(package): update package.json and package-lock.json to include @radix-ui/react-slider
This commit is contained in:
parent
fdc0403b81
commit
e488bad7c1
|
@ -0,0 +1,215 @@
|
|||
import { useState, useEffect, useRef } from "react"
|
||||
import { Pause, Play } from "lucide-react"
|
||||
import { Button } from "@/app/_components/ui/button"
|
||||
import { cn } from "@/app/_lib/utils"
|
||||
import { Slider } from "@/app/_components/ui/slider"
|
||||
import { getMonthName } from "@/app/_utils/common"
|
||||
|
||||
interface SmoothYearTimelineProps {
|
||||
startYear: number
|
||||
endYear: number
|
||||
onChange?: (year: number, month: number, progress: number) => void
|
||||
className?: string
|
||||
autoPlay?: boolean
|
||||
autoPlaySpeed?: number // Time to progress through one month in ms
|
||||
}
|
||||
|
||||
export function SmoothYearTimeline({
|
||||
startYear = 2020,
|
||||
endYear = 2024,
|
||||
onChange,
|
||||
className,
|
||||
autoPlay = true,
|
||||
autoPlaySpeed = 1000, // Speed of month progress
|
||||
}: SmoothYearTimelineProps) {
|
||||
const [currentYear, setCurrentYear] = useState<number>(startYear)
|
||||
const [currentMonth, setCurrentMonth] = useState<number>(1) // Start at January (1)
|
||||
const [progress, setProgress] = useState<number>(0) // Progress within the current month
|
||||
const [isPlaying, setIsPlaying] = useState<boolean>(autoPlay)
|
||||
const [isDragging, setIsDragging] = useState<boolean>(false)
|
||||
const animationRef = useRef<number | null>(null)
|
||||
const lastUpdateTimeRef = useRef<number>(0)
|
||||
|
||||
// Calculate total months from start to end year
|
||||
const totalMonths = ((endYear - startYear) * 12) + 12 // +12 to include all months of end year
|
||||
|
||||
const calculateOverallProgress = (): number => {
|
||||
const yearDiff = currentYear - startYear
|
||||
const monthProgress = (yearDiff * 12) + (currentMonth - 1)
|
||||
return ((monthProgress + progress) / (totalMonths - 1)) * 100
|
||||
}
|
||||
|
||||
const calculateTimeFromProgress = (overallProgress: number): { year: number; month: number; progress: number } => {
|
||||
const totalProgress = (overallProgress * (totalMonths - 1)) / 100
|
||||
const monthsFromStart = Math.floor(totalProgress)
|
||||
|
||||
const year = startYear + Math.floor(monthsFromStart / 12)
|
||||
const month = (monthsFromStart % 12) + 1 // 1-12 for months
|
||||
const monthProgress = totalProgress - Math.floor(totalProgress)
|
||||
|
||||
return {
|
||||
year: Math.min(year, endYear),
|
||||
month: Math.min(month, 12),
|
||||
progress: monthProgress
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the current position for the active marker
|
||||
const calculateMarkerPosition = (): string => {
|
||||
const overallProgress = calculateOverallProgress()
|
||||
return `${overallProgress}%`
|
||||
}
|
||||
|
||||
const animate = (timestamp: number) => {
|
||||
if (!lastUpdateTimeRef.current) {
|
||||
lastUpdateTimeRef.current = timestamp
|
||||
}
|
||||
|
||||
if (!isDragging) {
|
||||
const elapsed = timestamp - lastUpdateTimeRef.current
|
||||
const progressIncrement = elapsed / autoPlaySpeed
|
||||
|
||||
let newProgress = progress + progressIncrement
|
||||
let newMonth = currentMonth
|
||||
let newYear = currentYear
|
||||
|
||||
if (newProgress >= 1) {
|
||||
newProgress = 0
|
||||
newMonth = currentMonth + 1
|
||||
|
||||
if (newMonth > 12) {
|
||||
newMonth = 1
|
||||
newYear = currentYear + 1
|
||||
|
||||
if (newYear > endYear) {
|
||||
newYear = startYear
|
||||
newMonth = 1
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentMonth(newMonth)
|
||||
setCurrentYear(newYear)
|
||||
}
|
||||
|
||||
setProgress(newProgress)
|
||||
if (onChange) {
|
||||
onChange(newYear, newMonth, newProgress)
|
||||
}
|
||||
|
||||
lastUpdateTimeRef.current = timestamp
|
||||
}
|
||||
|
||||
if (isPlaying) {
|
||||
animationRef.current = requestAnimationFrame(animate)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
lastUpdateTimeRef.current = 0
|
||||
animationRef.current = requestAnimationFrame(animate)
|
||||
} else if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current)
|
||||
}
|
||||
}
|
||||
}, [isPlaying, currentYear, currentMonth, progress, isDragging])
|
||||
|
||||
const handlePlayPause = () => {
|
||||
setIsPlaying(!isPlaying)
|
||||
}
|
||||
|
||||
const handleSliderChange = (value: number[]) => {
|
||||
const overallProgress = value[0]
|
||||
const { year, month, progress } = calculateTimeFromProgress(overallProgress)
|
||||
|
||||
setCurrentYear(year)
|
||||
setCurrentMonth(month)
|
||||
setProgress(progress)
|
||||
|
||||
if (onChange) {
|
||||
onChange(year, month, progress)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSliderDragStart = () => {
|
||||
setIsDragging(true)
|
||||
}
|
||||
|
||||
const handleSliderDragEnd = () => {
|
||||
setIsDragging(false)
|
||||
}
|
||||
|
||||
// Create year markers
|
||||
const yearMarkers = []
|
||||
for (let year = startYear; year <= endYear; year++) {
|
||||
yearMarkers.push(year)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("w-full bg-transparent text-emerald-500", className)}>
|
||||
<div className="relative">
|
||||
{/* Current month/year marker that moves with the slider */}
|
||||
<div
|
||||
className="absolute bottom-full mb-2 transform -translate-x-1/2 bg-emerald-500 text-background px-3 py-1 rounded-full text-xs font-bold z-20"
|
||||
style={{ left: calculateMarkerPosition() }}
|
||||
>
|
||||
{getMonthName(currentMonth)} {currentYear}
|
||||
</div>
|
||||
|
||||
{/* Wrap button and slider in their container */}
|
||||
<div className="px-2 flex gap-x-2">
|
||||
{/* Play/Pause button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={handlePlayPause}
|
||||
className="text-background bg-emerald-500 rounded-full hover:text-background hover:bg-emerald-500/50 h-10 w-10 z-10"
|
||||
>
|
||||
{isPlaying ? <Pause className="h-5 w-5" /> : <Play className="h-5 w-5" />}
|
||||
</Button>
|
||||
|
||||
{/* Slider */}
|
||||
<Slider
|
||||
value={[calculateOverallProgress()]}
|
||||
min={0}
|
||||
max={100}
|
||||
step={0.01}
|
||||
onValueChange={handleSliderChange}
|
||||
onValueCommit={handleSliderDragEnd}
|
||||
onPointerDown={handleSliderDragStart}
|
||||
className="w-full [&>span:first-child]:h-1.5 [&>span:first-child]:bg-white/30 [&_[role=slider]]:bg-emerald-500 [&_[role=slider]]:w-3 [&_[role=slider]]:h-3 [&_[role=slider]]:border-0 [&>span:first-child_span]:bg-emerald-500 [&_[role=slider]:focus-visible]:ring-0 [&_[role=slider]:focus-visible]:ring-offset-0 [&_[role=slider]:focus-visible]:scale-105 [&_[role=slider]:focus-visible]:transition-transform"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Year markers */}
|
||||
<div className="flex items-center relative h-10">
|
||||
<div className="absolute inset-0 h-full flex">
|
||||
{yearMarkers.map((year, index) => (
|
||||
<div
|
||||
key={year}
|
||||
className={cn(
|
||||
"flex-1 h-full flex items-center justify-center relative",
|
||||
index < yearMarkers.length - 1 && ""
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"text-sm transition-colors font-medium",
|
||||
year === currentYear ? "text-emerald-500 font-bold text-lg" : "text-white/50"
|
||||
)}
|
||||
>
|
||||
{year}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -20,6 +20,7 @@ import SidebarToggle from "./sidebar/sidebar-toggle"
|
|||
import { cn } from "@/app/_lib/utils"
|
||||
import CrimePopup from "./pop-up/crime-popup"
|
||||
import { $Enums, crime_categories, crime_incidents, crimes, demographics, districts, geographics, locations } from "@prisma/client"
|
||||
import { SmoothYearTimeline } from "./controls/year-timeline"
|
||||
|
||||
// Updated CrimeIncident type to match the structure in crime_incidents
|
||||
interface CrimeIncident {
|
||||
|
@ -44,6 +45,7 @@ export default function CrimeMap() {
|
|||
const [selectedYear, setSelectedYear] = useState<number>(2024)
|
||||
const [selectedMonth, setSelectedMonth] = useState<number | "all">("all")
|
||||
const [activeControl, setActiveControl] = useState<ITopTooltipsMapId>("incidents")
|
||||
const [yearProgress, setYearProgress] = useState(0)
|
||||
|
||||
const mapContainerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
|
@ -174,6 +176,13 @@ export default function CrimeMap() {
|
|||
setSelectedDistrict(feature);
|
||||
}
|
||||
|
||||
// Handle year-month timeline change
|
||||
const handleTimelineChange = useCallback((year: number, month: number, progress: number) => {
|
||||
setSelectedYear(year)
|
||||
setSelectedMonth(month)
|
||||
setYearProgress(progress)
|
||||
}, [])
|
||||
|
||||
// Reset filters
|
||||
const resetFilters = useCallback(() => {
|
||||
setSelectedYear(2024)
|
||||
|
@ -295,8 +304,21 @@ export default function CrimeMap() {
|
|||
/>
|
||||
|
||||
<MapLegend position="bottom-right" />
|
||||
|
||||
</>
|
||||
)}
|
||||
|
||||
{isFullscreen && (
|
||||
<div className="absolute flex w-full bottom-0">
|
||||
<SmoothYearTimeline
|
||||
startYear={2020}
|
||||
endYear={2024}
|
||||
autoPlay={false}
|
||||
autoPlaySpeed={1000}
|
||||
onChange={handleTimelineChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</MapView>
|
||||
</div>
|
||||
</div>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@ import type React from "react"
|
|||
import { useState, useCallback, useRef } from "react"
|
||||
import { type ViewState, Map, type MapRef, NavigationControl, GeolocateControl } from "react-map-gl/mapbox"
|
||||
import { FullscreenControl } from "react-map-gl/mapbox"
|
||||
import { BASE_LATITUDE, BASE_LONGITUDE, BASE_ZOOM, MAPBOX_STYLES, type MapboxStyle } from "@/app/_utils/const/map"
|
||||
import { BASE_BEARING, BASE_LATITUDE, BASE_LONGITUDE, BASE_PITCH, BASE_ZOOM, MAPBOX_STYLES, type MapboxStyle } from "@/app/_utils/const/map"
|
||||
import "mapbox-gl/dist/mapbox-gl.css"
|
||||
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
|
||||
import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
|
||||
|
@ -39,8 +39,8 @@ export default function MapView({
|
|||
longitude: BASE_LONGITUDE,
|
||||
latitude: BASE_LATITUDE,
|
||||
zoom: BASE_ZOOM,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
bearing: BASE_BEARING,
|
||||
pitch: BASE_PITCH,
|
||||
...initialViewState,
|
||||
}
|
||||
|
||||
|
|
|
@ -76,13 +76,13 @@ export default function DistrictPopup({
|
|||
const getCrimeRateBadge = (level?: string) => {
|
||||
switch (level) {
|
||||
case "low":
|
||||
return <Badge className="bg-emerald-600 text-white">Low</Badge>
|
||||
return <Badge className="bg-emerald-600 text-white hover:bg-emerald-600">Low</Badge>
|
||||
case "medium":
|
||||
return <Badge className="bg-amber-500 text-white">Medium</Badge>
|
||||
return <Badge className="bg-amber-500 text-white hover:bg-amber-500">Medium</Badge>
|
||||
case "high":
|
||||
return <Badge className="bg-rose-600 text-white">High</Badge>
|
||||
return <Badge className="bg-rose-600 text-white hover:bg-rose-600">High</Badge>
|
||||
case "critical":
|
||||
return <Badge className="bg-red-700 text-white">Critical</Badge>
|
||||
return <Badge className="bg-red-700 text-white hover:bg-red-700">Critical</Badge>
|
||||
default:
|
||||
return <Badge className="bg-slate-600">Unknown</Badge>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider"
|
||||
import { cn } from "@/app/_lib/utils"
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
))
|
||||
Slider.displayName = SliderPrimitive.Root.displayName
|
||||
|
||||
export { Slider }
|
|
@ -1,6 +1,6 @@
|
|||
export const MAP_STYLE = 'mapbox://styles/mapbox/dark-v11';
|
||||
|
||||
export const BASE_ZOOM = 9.5; // Default zoom level for the map
|
||||
export const BASE_PITCH = 0; // Default pitch for the map
|
||||
export const BASE_BEARING = 0; // Default bearing for the map
|
||||
export const BASE_LATITUDE = -8.17; // Default latitude for the map center (Jember region)
|
||||
export const BASE_LONGITUDE = 113.65; // Default longitude for the map center (Jember region)
|
||||
export const MAPBOX_ACCESS_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-separator": "^1.1.2",
|
||||
"@radix-ui/react-slider": "^1.3.2",
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-switch": "^1.1.3",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
|
@ -3442,6 +3443,230 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.2.tgz",
|
||||
"integrity": "sha512-oQnqfgSiYkxZ1MrF6672jw2/zZvpB+PJsrIc3Zm1zof1JHf/kj7WhmROw7JahLfOwYQ5/+Ip0rFORgF1tjSiaQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/number": "1.1.1",
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/react-collection": "1.1.4",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/number": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
|
||||
"integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
|
||||
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-collection": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz",
|
||||
"integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.0",
|
||||
"@radix-ui/react-slot": "1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-direction": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz",
|
||||
"integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-previous": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
|
||||
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-size": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
|
||||
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||
|
@ -3586,6 +3811,39 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-effect-event": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
||||
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
"@radix-ui/react-scroll-area": "^1.2.3",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-separator": "^1.1.2",
|
||||
"@radix-ui/react-slider": "^1.3.2",
|
||||
"@radix-ui/react-slot": "^1.1.2",
|
||||
"@radix-ui/react-switch": "^1.1.3",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
|
|
Loading…
Reference in New Issue