diff --git a/package.json b/package.json
index a0d9f09..d320166 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"next": "14.2.5",
"react": "^18",
"react-dom": "^18",
+ "react-hls-player": "^3.0.7",
"tailwind-scrollbar-hide": "^1.1.7",
"tailwindcss-animate": "^1.0.7",
"ts-pattern": "^5.2.0",
diff --git a/src/app/globals.css b/src/app/globals.css
index 4068e95..c7e947b 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -1,3 +1,5 @@
+@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard-dynamic-subset.min.css');
+
@tailwind base;
@tailwind components;
@tailwind utilities;
@@ -91,11 +93,19 @@
}
html,
body {
+ @apply font-sans;
touch-action: none;
}
+ body.video-active * {
+ pointer-events: none;
+ }
+
+ body.video-active .video-close {
+ pointer-events: auto;
+ }
}
-@layer typography {
+@layer base {
/* Headings */
.h1 {
@apply text-3xl font-bold leading-[1.6rem] tracking-[0.02rem];
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 0ffad46..e3eb500 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,11 +1,8 @@
import '@/app/globals.css';
import type { Metadata } from 'next';
-import { Inter } from 'next/font/google';
import Providers from './_providers';
-const inter = Inter({ subsets: ['latin'] });
-
export const metadata: Metadata = {
title: 'WeSki',
description: 'We Ski',
@@ -14,7 +11,7 @@ export const metadata: Metadata = {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
-
+
{children}
diff --git a/src/app/mobile/congestion/page.tsx b/src/app/mobile/congestion/page.tsx
deleted file mode 100644
index 3468067..0000000
--- a/src/app/mobile/congestion/page.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@/pages/congestion/ui/congestion-page';
diff --git a/src/app/mobile/slop-status/page.tsx b/src/app/mobile/slop-status/page.tsx
new file mode 100644
index 0000000..834c97b
--- /dev/null
+++ b/src/app/mobile/slop-status/page.tsx
@@ -0,0 +1 @@
+export { default } from '@/pages/slop-status/ui/slop-status-page';
diff --git a/src/app/mobile/weather/layout.tsx b/src/app/mobile/weather/layout.tsx
deleted file mode 100644
index b50fc0f..0000000
--- a/src/app/mobile/weather/layout.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { NavBar, StatusBar } from '@/widgets/header/ui';
-import { cn } from '@/shared/lib';
-
-export default function Layout({ children }: { children: React.ReactNode }) {
- return (
-
-
-
-
WeSki
- {children}
-
-
-
- );
-}
diff --git a/src/entities/slop/model/jisan.tsx b/src/entities/slop/model/jisan.tsx
index 8a12518..9d9ee58 100644
--- a/src/entities/slop/model/jisan.tsx
+++ b/src/entities/slop/model/jisan.tsx
@@ -19,6 +19,9 @@ export const JISAN: ResortInfo = {
Element: Lemon1LiftPath,
webcam: null,
isOpen: false,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
{
id: 'lemon1-1-lift',
@@ -27,6 +30,9 @@ export const JISAN: ResortInfo = {
Element: Lemon1Sub1LiftPath,
webcam: null,
isOpen: false,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
{
id: 'orange2-lift',
@@ -40,8 +46,12 @@ export const JISAN: ResortInfo = {
top: 'top-[69%]',
left: 'left-[29%]',
},
+ src: 'http://konjiam.live.cdn.cloudn.co.kr/konjiam/cam01.stream/playlist.m3u8',
},
isOpen: true,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
{
id: 'orange3-lift',
@@ -55,8 +65,12 @@ export const JISAN: ResortInfo = {
top: 'top-[64%]',
left: 'left-[38%]',
},
+ src: 'http://konjiam.live.cdn.cloudn.co.kr/konjiam/cam01.stream/playlist.m3u8',
},
isOpen: true,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
{
id: 'new-orange-lift',
@@ -65,6 +79,9 @@ export const JISAN: ResortInfo = {
Element: NewOrangeLiftPath,
webcam: null,
isOpen: false,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
{
id: 'blue-lift',
@@ -80,6 +97,9 @@ export const JISAN: ResortInfo = {
},
},
isOpen: true,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
{
id: 'silver6-lift',
@@ -88,6 +108,9 @@ export const JISAN: ResortInfo = {
Element: Silver6LiftPath,
webcam: null,
isOpen: false,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
{
id: 'silver7-lift',
@@ -96,6 +119,9 @@ export const JISAN: ResortInfo = {
Element: Silver7LiftPath,
webcam: null,
isOpen: false,
+ isDayOpen: false,
+ isNightOpen: false,
+ isLateNightOpen: false,
},
],
};
diff --git a/src/entities/slop/model/model.d.ts b/src/entities/slop/model/model.d.ts
index bdf2946..dfc4639 100644
--- a/src/entities/slop/model/model.d.ts
+++ b/src/entities/slop/model/model.d.ts
@@ -14,6 +14,10 @@ export type ResortInfo = {
name: string;
Element: React.FC;
isOpen: boolean;
+ isDayOpen: boolean;
+ isNightOpen: boolean;
+ isLateNightOpen: boolean;
+
webcam: {
id: string;
name: string;
diff --git a/src/entities/slop/ui/level-chip.tsx b/src/entities/slop/ui/level-chip.tsx
index 271509e..d6511ed 100644
--- a/src/entities/slop/ui/level-chip.tsx
+++ b/src/entities/slop/ui/level-chip.tsx
@@ -30,18 +30,20 @@ const LEVEL: Record = {
};
interface LevelChipProps {
+ className?: string;
level: Level;
}
-const LevelChip = ({ level }: LevelChipProps) => {
+const LevelChip = ({ level, className }: LevelChipProps) => {
return (
-
{LEVEL[level].text}
+
{LEVEL[level].text}
);
};
diff --git a/src/widgets/webcam/hooks/useMapPinch.ts b/src/features/slop/hooks/useMapPinch.ts
similarity index 89%
rename from src/widgets/webcam/hooks/useMapPinch.ts
rename to src/features/slop/hooks/useMapPinch.ts
index 1200fc4..91f8cbc 100644
--- a/src/widgets/webcam/hooks/useMapPinch.ts
+++ b/src/features/slop/hooks/useMapPinch.ts
@@ -32,10 +32,8 @@ const useMapPinch = (containerRef: RefObject) => {
},
onDrag: ({ pinching, cancel, offset: [x, y] }) => {
if (pinching) return cancel();
- api.start({ x, y });
- },
- onDragEnd: () => {
- const [boundedX, boundedY] = getBoundedPositions(
+
+ const [{ min: minX, max: maxX }, { min: minY, max: maxY }] = getBoundedPositions(
{
x: style.x.get(),
y: style.y.get(),
@@ -46,6 +44,10 @@ const useMapPinch = (containerRef: RefObject) => {
height: containerRef.current!.getBoundingClientRect().height,
}
);
+
+ const boundedX = Math.min(Math.max(x, minX), maxX);
+ const boundedY = Math.min(Math.max(y, minY), maxY);
+
api.start({ x: boundedX, y: boundedY });
},
},
diff --git a/src/features/slop/ui/slop-camera.tsx b/src/features/slop/ui/slop-camera.tsx
new file mode 100644
index 0000000..e05fc48
--- /dev/null
+++ b/src/features/slop/ui/slop-camera.tsx
@@ -0,0 +1,46 @@
+import React, { useState } from 'react';
+import { createPortal } from 'react-dom';
+import { cn } from '@/shared/lib';
+import CameraButton from '@/shared/ui/cam-button';
+import { Tooltip } from '@/shared/ui/tooltip';
+import SlopVideo from './slop-video';
+
+interface SlopWebcamProps {
+ id: string;
+ name: string;
+ position: {
+ top: string;
+ left: string;
+ };
+ videoSrc?: string;
+ isOpen: boolean;
+ renderTarget: React.RefObject;
+}
+
+const SlopCamera = ({ name, position, isOpen, videoSrc, renderTarget }: SlopWebcamProps) => {
+ const [isVideoOpen, setIsVideoOpen] = useState(false);
+
+ const toggleVideo = () => {
+ setIsVideoOpen((pre) => !pre);
+ };
+
+ return (
+ <>
+
+
+
} isOpen={isOpen}>
+
+ {name}
+
+
+
+
+ {renderTarget?.current &&
+ isVideoOpen &&
+ videoSrc &&
+ createPortal(, renderTarget.current)}
+ >
+ );
+};
+
+export default SlopCamera;
diff --git a/src/features/slop/ui/slop-map.tsx b/src/features/slop/ui/slop-map.tsx
new file mode 100644
index 0000000..c6bfb11
--- /dev/null
+++ b/src/features/slop/ui/slop-map.tsx
@@ -0,0 +1,54 @@
+import { animated } from '@react-spring/web';
+import type { StaticImageData } from 'next/image';
+import Image from 'next/image';
+import type { ComponentType } from 'react';
+import React from 'react';
+import type { Level } from '@/entities/slop/model/model';
+import { cn } from '@/shared/lib';
+import useMapPinch from '../hooks/useMapPinch';
+
+interface SlopMapProps {
+ containerRef: React.RefObject;
+ children?: React.ReactNode;
+ mapSrc: StaticImageData;
+
+ slops: {
+ id: string;
+ level: Level;
+ Element: ComponentType<{
+ color?: string;
+ }>;
+ }[];
+ selectedSlop: string | null;
+}
+
+const SlopMap = ({ containerRef, children, mapSrc, slops, selectedSlop }: SlopMapProps) => {
+ const { ref, style } = useMapPinch(containerRef);
+
+ return (
+
+
+
+ {slops.map((slop) => (
+
+
+
+ ))}
+
+ {children}
+
+ );
+};
+
+export default SlopMap;
diff --git a/src/features/slop/ui/slop-status-list.tsx b/src/features/slop/ui/slop-status-list.tsx
new file mode 100644
index 0000000..f88af8d
--- /dev/null
+++ b/src/features/slop/ui/slop-status-list.tsx
@@ -0,0 +1,79 @@
+import React from 'react';
+import type { Level } from '@/entities/slop/model/model';
+import LevelChip from '@/entities/slop/ui/level-chip';
+import CheckIcon from '@/shared/icons/check';
+import CloseIcon from '@/shared/icons/close';
+import { cn } from '@/shared/lib';
+
+interface SlopStatusListProps {
+ list: {
+ id: string;
+ name: string;
+ level: Level;
+ isDayOpen: boolean;
+ isNightOpen: boolean;
+ isLateNightOpen: boolean;
+ }[];
+ selectedSlop: string | null;
+ setSelectedSlop: React.Dispatch>;
+}
+
+const SlopStatusList = ({ list, setSelectedSlop, selectedSlop }: SlopStatusListProps) => {
+ const handleSlopClick = ({ id }: { id: string }) => {
+ setSelectedSlop(id);
+ };
+ return (
+
+
+ {/* 슬로프명 */}
+ {/* 난이도 */}
+ {/* 주간 */}
+ {/* 야간 */}
+ {/* 심야 */}
+
+
+
+ 슬로프명 |
+ 난이도 |
+ 주간 |
+ 야간 |
+ 심야 |
+
+
+
+ {list.map((item, index) => (
+ handleSlopClick({ id: item.id })}
+ >
+
+ {item.name}
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+ ))}
+
+
+ );
+};
+
+export default SlopStatusList;
+
+const StatusIcon = ({ isOpen, className }: { isOpen: boolean; className?: string }) =>
+ isOpen ? (
+
+ ) : (
+
+ );
diff --git a/src/features/slop/ui/slop-video.tsx b/src/features/slop/ui/slop-video.tsx
new file mode 100644
index 0000000..da5e96d
--- /dev/null
+++ b/src/features/slop/ui/slop-video.tsx
@@ -0,0 +1,44 @@
+import React, { useEffect } from 'react';
+import ReactHlsPlayer from 'react-hls-player';
+import { cn } from '@/shared/lib';
+import CloseButton from '@/shared/ui/close-button';
+
+interface SlopVideoProps {
+ src: string;
+ closeVideo: () => void;
+}
+
+const SlopVideo = ({ src, closeVideo }: SlopVideoProps) => {
+ const playerRef = React.useRef(null);
+
+ useEffect(() => {
+ const player = playerRef.current;
+
+ function fireOnVideoStart() {
+ document.body.classList.add('video-active');
+ player?.focus();
+ }
+
+ player?.addEventListener('play', fireOnVideoStart);
+
+ return () => {
+ player?.removeEventListener('play', fireOnVideoStart);
+ document.body.classList.remove('video-active');
+ };
+ }, [playerRef]);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default SlopVideo;
diff --git a/src/pages/congestion/ui/congestion-page.tsx b/src/pages/congestion/ui/congestion-page.tsx
deleted file mode 100644
index f5b0bb8..0000000
--- a/src/pages/congestion/ui/congestion-page.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import Image from 'next/image';
-
-import { cn } from '@/shared/lib';
-
-const CongestionPage = () => {
- return (
-
- {Array.from({ length: 4 }).map((_, index) => (
-
- ))}
-
- );
-};
-
-export default CongestionPage;
diff --git a/src/pages/slop-status/ui/slop-status-page.tsx b/src/pages/slop-status/ui/slop-status-page.tsx
new file mode 100644
index 0000000..7c91d61
--- /dev/null
+++ b/src/pages/slop-status/ui/slop-status-page.tsx
@@ -0,0 +1,35 @@
+'use client';
+
+import { useRef, useState } from 'react';
+import SlopStatusHeader from '@/widgets/header/ui/slop-status-header';
+import SlopMap from '@/features/slop/ui/slop-map';
+import SlopStatusList from '@/features/slop/ui/slop-status-list';
+import { JISAN } from '@/entities/slop/model';
+import { cn } from '@/shared/lib';
+
+const SlopStatusPage = () => {
+ const DUMMY = JISAN;
+ const ref = useRef(null);
+
+ const [selectedSlop, setSelectedSlop] = useState(null);
+ return (
+
+
+
+
+
+ );
+};
+
+export default SlopStatusPage;
diff --git a/src/pages/webcam/ui/webcam-map-page.tsx b/src/pages/webcam/ui/webcam-map-page.tsx
deleted file mode 100644
index 5e58b04..0000000
--- a/src/pages/webcam/ui/webcam-map-page.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-'use client';
-
-import { animated } from '@react-spring/web';
-import Image from 'next/image';
-import React from 'react';
-
-import Summary from '@/widgets/weather/ui/summary';
-import useMapPinch from '@/widgets/webcam/hooks/useMapPinch';
-import type { Spot } from '@/entities/resort/model';
-import { ResortList } from '@/entities/resort/model';
-import LevelChip from '@/entities/slop/ui/level-chip';
-import { cn } from '@/shared/lib';
-
-const WebCamMapPage = () => {
- const [selectedTab, setSelectedTab] = React.useState(ResortList[0]);
- const [selectedSpot, setSelectedSpot] = React.useState(null);
- const { ref, style } = useMapPinch();
-
- return (
-
-
-
- {ResortList.map((tab) => (
-
{
- setSelectedTab(tab);
- setSelectedSpot(null);
- }}
- >
- {tab.name}
-
- ))}
-
-
tab.name === selectedTab.name)!} />
-
-
-
-
- selectedTab?.spots[0].isAvailable && setSelectedSpot(selectedTab.spots[0])
- }
- />
-
- {selectedSpot && (
-
-
-
- )}
-
-
- {selectedTab.spots?.map((spot) => (
-
{
- spot.isAvailable && setSelectedSpot(spot);
- }}
- >
-
{spot.name}
-
-
- ))}
-
-
- );
-};
-
-export default WebCamMapPage;
diff --git a/src/pages/webcam/ui/webcam-mobile-map-page.tsx b/src/pages/webcam/ui/webcam-mobile-map-page.tsx
index 8df5066..c6cc746 100644
--- a/src/pages/webcam/ui/webcam-mobile-map-page.tsx
+++ b/src/pages/webcam/ui/webcam-mobile-map-page.tsx
@@ -16,6 +16,7 @@ const WebCamMobileMapPage = () => {
...item,
isWebcam: !!item.webcam,
}))}
+ selectedSlop={selectedSlop}
setSelectedSlop={setSelectedSlop}
/>
diff --git a/src/shared/icons/check.tsx b/src/shared/icons/check.tsx
new file mode 100644
index 0000000..d346d04
--- /dev/null
+++ b/src/shared/icons/check.tsx
@@ -0,0 +1,30 @@
+import type { SVGProps } from 'react';
+import React from 'react';
+
+interface CheckIconProps extends SVGProps {
+ className?: string;
+}
+
+const CheckIcon = ({ className, ...props }: CheckIconProps) => {
+ return (
+
+ );
+};
+
+export default CheckIcon;
diff --git a/src/shared/icons/close.tsx b/src/shared/icons/close.tsx
new file mode 100644
index 0000000..abe5652
--- /dev/null
+++ b/src/shared/icons/close.tsx
@@ -0,0 +1,29 @@
+import type { SVGProps } from 'react';
+import React from 'react';
+
+interface CloseIconProps extends SVGProps {
+ className?: string;
+}
+
+const CloseIcon = ({ className, ...args }: CloseIconProps) => {
+ return (
+
+ );
+};
+
+export default CloseIcon;
diff --git a/src/shared/lib/getBoundedPositions.ts b/src/shared/lib/getBoundedPositions.ts
index ec4af9a..6f42d91 100644
--- a/src/shared/lib/getBoundedPositions.ts
+++ b/src/shared/lib/getBoundedPositions.ts
@@ -24,12 +24,7 @@ const getBoundedPositions = (
},
};
- const boundedPosition = {
- x: Math.max(bounds.x.min, Math.min(bounds.x.max, position.x)),
- y: Math.max(bounds.y.min, Math.min(bounds.y.max, position.y)),
- };
-
- return [boundedPosition.x, boundedPosition.y];
+ return [bounds.x, bounds.y];
};
export default getBoundedPositions;
diff --git a/src/shared/ui/close-button.tsx b/src/shared/ui/close-button.tsx
new file mode 100644
index 0000000..2395357
--- /dev/null
+++ b/src/shared/ui/close-button.tsx
@@ -0,0 +1,26 @@
+import React, { forwardRef } from 'react';
+import CloseIcon from '../icons/close';
+import { cn } from '../lib';
+
+type CloseButtonProps = React.ButtonHTMLAttributes;
+
+const CloseButton = forwardRef(
+ ({ className, ...args }, ref) => {
+ return (
+
+ );
+ }
+);
+
+CloseButton.displayName = 'CloseButton';
+
+export default CloseButton;
diff --git a/src/shared/ui/tooltip.tsx b/src/shared/ui/tooltip.tsx
index 33661c8..f6cd58f 100644
--- a/src/shared/ui/tooltip.tsx
+++ b/src/shared/ui/tooltip.tsx
@@ -46,9 +46,11 @@ export const Tooltip = ({ trigger, children, isOpen }: TooltipProps) => {
}}
>
{trigger}
-
- {children}
-
+
+
+ {children}
+
+
);
};
diff --git a/src/widgets/header/ui/slop-status-header.tsx b/src/widgets/header/ui/slop-status-header.tsx
new file mode 100644
index 0000000..e93d04a
--- /dev/null
+++ b/src/widgets/header/ui/slop-status-header.tsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import { cn } from '@/shared/lib';
+
+const SlopStatusHeader = () => {
+ return (
+
+
슬로프 운행 현황
+
10월 21일 23:00 업데이트
+
+ );
+};
+
+export default SlopStatusHeader;
diff --git a/src/widgets/webcam/ui/webcam-map.tsx b/src/widgets/webcam/ui/webcam-map.tsx
index 55fd55a..a918c73 100644
--- a/src/widgets/webcam/ui/webcam-map.tsx
+++ b/src/widgets/webcam/ui/webcam-map.tsx
@@ -1,14 +1,10 @@
-import { animated } from '@react-spring/web';
import type { StaticImageData } from 'next/image';
-import Image from 'next/image';
import type { ComponentType } from 'react';
import React, { useRef } from 'react';
+import SlopCamera from '@/features/slop/ui/slop-camera';
+import SlopMap from '@/features/slop/ui/slop-map';
import type { Level } from '@/entities/slop/model/model';
import { cn } from '@/shared/lib';
-import CameraButton from '@/shared/ui/cam-button';
-
-import { Tooltip } from '@/shared/ui/tooltip';
-import useMapPinch from '../hooks/useMapPinch';
interface WebcamMapProps {
slops: {
@@ -24,6 +20,7 @@ interface WebcamMapProps {
top: string;
left: string;
};
+ src?: string;
} | null;
}[];
mapSrc: StaticImageData;
@@ -32,48 +29,35 @@ interface WebcamMapProps {
const WebcamMap = ({ slops, mapSrc, selectedSlop }: WebcamMapProps) => {
const containerRef = useRef(null);
- const { ref, style } = useMapPinch(containerRef);
return (
-
-
- {slops.map((slop) => {
- return (
-
-
-
-
- {slop.webcam && (
-
-
} isOpen={selectedSlop === slop.id}>
-
{slop.webcam.name}
-
-
- )}
-
- );
- })}
-
+ {slops
+ .filter(
+ (
+ slop
+ ): slop is WebcamMapProps['slops'][number] & {
+ webcam: NonNullable;
+ } => slop.webcam !== null
+ )
+ .map(({ id, webcam }) => (
+
+ ))}
+
);
};
diff --git a/src/widgets/webcam/ui/webcam-slop-list.tsx b/src/widgets/webcam/ui/webcam-slop-list.tsx
index cf804bc..6976421 100644
--- a/src/widgets/webcam/ui/webcam-slop-list.tsx
+++ b/src/widgets/webcam/ui/webcam-slop-list.tsx
@@ -13,12 +13,17 @@ interface WebcamSlopListProps {
isOpen: boolean;
isWebcam: boolean;
}[];
+ selectedSlop: string | null;
setSelectedSlop: React.Dispatch>;
}
-const WebcamSlopList = ({ list, setSelectedSlop }: WebcamSlopListProps) => {
+const WebcamSlopList = ({ list, selectedSlop, setSelectedSlop }: WebcamSlopListProps) => {
const handleSlopClick = ({ id, isOpen }: { id: string; isOpen: boolean }) => {
if (!isOpen) return;
+ if (selectedSlop === id) {
+ setSelectedSlop(null);
+ return;
+ }
setSelectedSlop(id);
};
return (
@@ -28,7 +33,8 @@ const WebcamSlopList = ({ list, setSelectedSlop }: WebcamSlopListProps) => {
handleSlopClick(item)}
>
diff --git a/tailwind.config.ts b/tailwind.config.ts
index fc14ae2..2584159 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -19,6 +19,9 @@ const config = {
},
extend: {
+ fontFamily: {
+ sans: ['Pretendard', 'sans-serif'],
+ },
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
diff --git a/yarn.lock b/yarn.lock
index 05ca985..36f7632 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1555,6 +1555,11 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+eventemitter3@^4.0.3:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+ integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -1840,6 +1845,14 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
dependencies:
function-bind "^1.1.2"
+hls.js@^0.14.17:
+ version "0.14.17"
+ resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-0.14.17.tgz#0127cff2ec2f994a54eb955fe669ef6153a8e317"
+ integrity sha512-25A7+m6qqp6UVkuzUQ//VVh2EEOPYlOBg32ypr34bcPO7liBMOkKFvbjbCBfiPAOTA/7BSx1Dujft3Th57WyFg==
+ dependencies:
+ eventemitter3 "^4.0.3"
+ url-toolkit "^2.1.6"
+
ignore@^5.2.0, ignore@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
@@ -2624,6 +2637,13 @@ react-dom@^18:
loose-envify "^1.1.0"
scheduler "^0.23.2"
+react-hls-player@^3.0.7:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/react-hls-player/-/react-hls-player-3.0.7.tgz#1f66f55c2a5dc1fa6441ddc47b7c892079a276cf"
+ integrity sha512-i5QWNyLmaUhV/mgnpljRJT0CBfJnylClV/bne8aiXO3ZqU0+D3U/jtTDwdXM4i5qHhyFy9lemyZ179IgadKd0Q==
+ dependencies:
+ hls.js "^0.14.17"
+
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -3188,6 +3208,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+url-toolkit@^2.1.6:
+ version "2.2.5"
+ resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.2.5.tgz#58406b18e12c58803e14624df5e374f638b0f607"
+ integrity sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==
+
use-callback-ref@^1.3.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.2.tgz#6134c7f6ff76e2be0b56c809b17a650c942b1693"