This commit is contained in:
@@ -78,6 +78,7 @@
|
||||
"react-i18next": "^15.2.0",
|
||||
"react-icons": "^5.4.0",
|
||||
"react-responsive": "^10.0.0",
|
||||
"react-window": "^1.8.11",
|
||||
"semver": "^7.7.1",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"zustand": "5.0.1"
|
||||
@@ -91,6 +92,7 @@
|
||||
"@types/react": "18.3.12",
|
||||
"@types/react-color": "^3.0.13",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"autoprefixer": "^10.4.20",
|
||||
|
||||
@@ -70,14 +70,14 @@ const SidebarContent: React.FC<{
|
||||
<>
|
||||
<div
|
||||
className={clsx(
|
||||
'sidebar-content flex min-h-0 flex-grow flex-col shadow-inner',
|
||||
'sidebar-content flex h-full min-h-0 flex-grow flex-col shadow-inner',
|
||||
'font-sans text-base font-normal sm:text-sm',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className={clsx(
|
||||
'scroll-container overflow-y-auto transition-opacity duration-300 ease-in-out',
|
||||
'scroll-container min-h-0 flex-1 overflow-y-auto transition-opacity duration-300 ease-in-out',
|
||||
{ 'opacity-0': fade, 'opacity-100': !fade },
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useCallback } from 'react';
|
||||
import { ListChildComponentProps } from 'react-window';
|
||||
import { TOCItem } from '@/libs/document';
|
||||
import { getContentMd5 } from '@/utils/misc';
|
||||
|
||||
const createExpanderIcon = (isExpanded: boolean) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox='0 0 8 10'
|
||||
width='8'
|
||||
height='10'
|
||||
className={clsx(
|
||||
'text-base-content transform transition-transform',
|
||||
isExpanded ? 'rotate-90' : 'rotate-0',
|
||||
)}
|
||||
style={{ transformOrigin: 'center' }}
|
||||
fill='currentColor'
|
||||
>
|
||||
<polygon points='0 0, 8 5, 0 10' />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export interface FlatTOCItem {
|
||||
item: TOCItem;
|
||||
depth: number;
|
||||
index: number;
|
||||
isExpanded?: boolean;
|
||||
}
|
||||
|
||||
const TOCItemView = React.memo<{
|
||||
bookKey: string;
|
||||
flatItem: FlatTOCItem;
|
||||
itemSize?: number;
|
||||
isActive: boolean;
|
||||
onToggleExpand: (item: TOCItem) => void;
|
||||
onItemClick: (item: TOCItem) => void;
|
||||
}>(({ flatItem, itemSize, isActive, onToggleExpand, onItemClick }) => {
|
||||
const { item, depth } = flatItem;
|
||||
|
||||
const handleToggleExpand = useCallback(
|
||||
(event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onToggleExpand(item);
|
||||
},
|
||||
[item, onToggleExpand],
|
||||
);
|
||||
|
||||
const handleClickItem = useCallback(
|
||||
(event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
onItemClick(item);
|
||||
},
|
||||
[item, onItemClick],
|
||||
);
|
||||
|
||||
return (
|
||||
<span
|
||||
role='treeitem'
|
||||
tabIndex={-1}
|
||||
onClick={item.href ? handleClickItem : undefined}
|
||||
aria-expanded={flatItem.isExpanded ? 'true' : 'false'}
|
||||
aria-selected={isActive ? 'true' : 'false'}
|
||||
data-href={item.href ? getContentMd5(item.href) : undefined}
|
||||
className={clsx(
|
||||
'flex w-full cursor-pointer items-center rounded-md py-4 sm:py-2',
|
||||
isActive
|
||||
? 'sm:bg-base-300/85 sm:hover:bg-base-300 sm:text-base-content text-blue-500'
|
||||
: 'sm:hover:bg-base-300/85',
|
||||
)}
|
||||
style={{
|
||||
height: itemSize ? `${itemSize}px` : 'auto',
|
||||
paddingInlineStart: `${(depth + 1) * 12}px`,
|
||||
}}
|
||||
>
|
||||
{item.subitems && (
|
||||
<span
|
||||
onClick={handleToggleExpand}
|
||||
className='inline-block cursor-pointer'
|
||||
style={{
|
||||
padding: '12px',
|
||||
margin: '-12px',
|
||||
}}
|
||||
>
|
||||
{createExpanderIcon(flatItem.isExpanded || false)}
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className='ms-2 truncate text-ellipsis'
|
||||
style={{
|
||||
maxWidth: 'calc(100% - 24px)',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
{item.location && (
|
||||
<span className='text-base-content/50 ms-auto ps-1 text-xs sm:pe-1'>
|
||||
{item.location.current + 1}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
TOCItemView.displayName = 'TOCItemView';
|
||||
|
||||
interface ListRowProps {
|
||||
bookKey: string;
|
||||
flatItem: FlatTOCItem;
|
||||
itemSize?: number;
|
||||
activeHref: string | null;
|
||||
onToggleExpand: (item: TOCItem) => void;
|
||||
onItemClick: (item: TOCItem) => void;
|
||||
}
|
||||
|
||||
export const StaticListRow: React.FC<ListRowProps> = ({
|
||||
bookKey,
|
||||
flatItem,
|
||||
itemSize,
|
||||
activeHref,
|
||||
onToggleExpand,
|
||||
onItemClick,
|
||||
}) => {
|
||||
const isActive = activeHref === flatItem.item.href;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'border-base-300 w-full border-b sm:border-none',
|
||||
'pe-4 ps-2 pt-[1px] sm:pe-2',
|
||||
)}
|
||||
>
|
||||
<TOCItemView
|
||||
bookKey={bookKey}
|
||||
flatItem={flatItem}
|
||||
itemSize={itemSize}
|
||||
isActive={isActive}
|
||||
onToggleExpand={onToggleExpand}
|
||||
onItemClick={onItemClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const VirtualListRow: React.FC<
|
||||
ListChildComponentProps & {
|
||||
data: {
|
||||
bookKey: string;
|
||||
flatItems: FlatTOCItem[];
|
||||
itemSize: number;
|
||||
activeHref: string | null;
|
||||
onToggleExpand: (item: TOCItem) => void;
|
||||
onItemClick: (item: TOCItem) => void;
|
||||
};
|
||||
}
|
||||
> = ({ index, style, data }) => {
|
||||
const { flatItems, bookKey, activeHref, itemSize, onToggleExpand, onItemClick } = data;
|
||||
const flatItem = flatItems[index];
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<StaticListRow
|
||||
bookKey={bookKey}
|
||||
flatItem={flatItem}
|
||||
itemSize={itemSize - 1}
|
||||
activeHref={activeHref}
|
||||
onToggleExpand={onToggleExpand}
|
||||
onItemClick={onItemClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,198 +1,199 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FixedSizeList as VirtualList } from 'react-window';
|
||||
|
||||
import { TOCItem } from '@/libs/document';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useSidebarStore } from '@/store/sidebarStore';
|
||||
import { findParentPath } from '@/utils/toc';
|
||||
import { getContentMd5 } from '@/utils/misc';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { BookProgress } from '@/types/book';
|
||||
import { getContentMd5 } from '@/utils/misc';
|
||||
import { useTextTranslation } from '../../hooks/useTextTranslation';
|
||||
import { FlatTOCItem, StaticListRow, VirtualListRow } from './TOCItem';
|
||||
|
||||
const createExpanderIcon = (isExpanded: boolean) => {
|
||||
return (
|
||||
<svg
|
||||
viewBox='0 0 8 10'
|
||||
width='8'
|
||||
height='10'
|
||||
className={clsx(
|
||||
'text-base-content transform transition-transform',
|
||||
isExpanded ? 'rotate-90' : 'rotate-0',
|
||||
)}
|
||||
style={{ transformOrigin: 'center' }}
|
||||
fill='currentColor'
|
||||
>
|
||||
<polygon points='0 0, 8 5, 0 10' />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
const useFlattenedTOC = (toc: TOCItem[], expandedItems: Set<string>) => {
|
||||
return useMemo(() => {
|
||||
const flattenTOC = (items: TOCItem[], depth = 0): FlatTOCItem[] => {
|
||||
const result: FlatTOCItem[] = [];
|
||||
items.forEach((item, index) => {
|
||||
const isExpanded = expandedItems.has(item.href || '');
|
||||
result.push({ item, depth, index, isExpanded });
|
||||
if (item.subitems && isExpanded) {
|
||||
result.push(...flattenTOC(item.subitems, depth + 1));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
const TOCItemView: React.FC<{
|
||||
bookKey: string;
|
||||
item: TOCItem;
|
||||
depth: number;
|
||||
expandedItems: string[];
|
||||
}> = ({ bookKey, item, depth, expandedItems }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(expandedItems.includes(item.href || ''));
|
||||
const { getView, getProgress } = useReaderStore();
|
||||
const progress = getProgress(bookKey);
|
||||
|
||||
const handleToggleExpand = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setIsExpanded((prev) => !prev);
|
||||
};
|
||||
|
||||
const handleClickItem = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
eventDispatcher.dispatch('navigate', { bookKey, href: item.href });
|
||||
if (item.href) {
|
||||
getView(bookKey)?.goTo(item.href);
|
||||
}
|
||||
};
|
||||
|
||||
const isActive = progress ? progress.sectionHref === item.href : false;
|
||||
|
||||
useEffect(() => {
|
||||
setIsExpanded(expandedItems.includes(item.href || ''));
|
||||
}, [expandedItems, item.href]);
|
||||
|
||||
return (
|
||||
<li className='border-base-300 w-full border-b sm:border-none sm:pt-[1px]'>
|
||||
<span
|
||||
role='treeitem'
|
||||
tabIndex={-1}
|
||||
onClick={item.href ? handleClickItem : undefined}
|
||||
style={{ paddingInlineStart: `${(depth + 1) * 12}px` }}
|
||||
aria-expanded={isExpanded ? 'true' : 'false'}
|
||||
aria-selected={isActive ? 'true' : 'false'}
|
||||
data-href={item.href ? getContentMd5(item.href) : undefined}
|
||||
className={`flex w-full cursor-pointer items-center rounded-md py-4 sm:py-2 ${
|
||||
isActive
|
||||
? 'sm:bg-base-300/85 sm:hover:bg-base-300 sm:text-base-content text-blue-500'
|
||||
: 'sm:hover:bg-base-300/85'
|
||||
}`}
|
||||
>
|
||||
{item.subitems && (
|
||||
<span
|
||||
onClick={handleToggleExpand}
|
||||
className='inline-block cursor-pointer'
|
||||
style={{
|
||||
padding: '12px',
|
||||
margin: '-12px',
|
||||
}}
|
||||
>
|
||||
{createExpanderIcon(isExpanded)}
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className='ms-2 truncate text-ellipsis'
|
||||
style={{
|
||||
maxWidth: 'calc(100% - 24px)',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
{item.location && (
|
||||
<span className='text-base-content/50 ms-auto ps-1 text-xs sm:pe-1'>
|
||||
{item.location.current + 1}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
{item.subitems && isExpanded && (
|
||||
<ol role='group'>
|
||||
{item.subitems.map((subitem, index) => (
|
||||
<TOCItemView
|
||||
bookKey={bookKey}
|
||||
key={`${index}-${subitem.href}`}
|
||||
item={subitem}
|
||||
depth={depth + 1}
|
||||
expandedItems={expandedItems}
|
||||
/>
|
||||
))}
|
||||
</ol>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
return flattenTOC(toc);
|
||||
}, [toc, expandedItems]);
|
||||
};
|
||||
|
||||
const TOCView: React.FC<{
|
||||
bookKey: string;
|
||||
toc: TOCItem[];
|
||||
}> = ({ bookKey, toc }) => {
|
||||
const { getProgress } = useReaderStore();
|
||||
const { getView, getProgress, getViewSettings } = useReaderStore();
|
||||
const { sideBarBookKey, isSideBarVisible } = useSidebarStore();
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const progress = getProgress(bookKey);
|
||||
|
||||
const [expandedItems, setExpandedItems] = useState<string[]>([]);
|
||||
const viewRef = useRef<HTMLUListElement | null>(null);
|
||||
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set());
|
||||
const [containerHeight, setContainerHeight] = useState(400);
|
||||
|
||||
useTextTranslation(bookKey, viewRef.current);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const vitualListRef = useRef<VirtualList | null>(null);
|
||||
const staticListRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const expandParents = (toc: TOCItem[], href: string) => {
|
||||
const parentPath = findParentPath(toc, href).map((item) => item.href);
|
||||
setExpandedItems(parentPath.filter(Boolean) as string[]);
|
||||
};
|
||||
|
||||
const scrollToProgress = (progress: BookProgress) => {
|
||||
const { sectionHref: currentHref } = progress;
|
||||
const hrefMd5 = currentHref ? getContentMd5(currentHref) : '';
|
||||
const currentItem = viewRef.current?.querySelector(`[data-href="${hrefMd5}"]`);
|
||||
if (currentItem) {
|
||||
const rect = currentItem.getBoundingClientRect();
|
||||
const isVisible = rect.top >= 0 && rect.bottom <= window.innerHeight;
|
||||
if (!isVisible) {
|
||||
(currentItem as HTMLElement).scrollIntoView({ behavior: 'instant', block: 'center' });
|
||||
}
|
||||
(currentItem as HTMLElement).setAttribute('aria-current', 'page');
|
||||
}
|
||||
};
|
||||
useTextTranslation(bookKey, containerRef.current);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new MutationObserver(() => {
|
||||
const progress = getProgress(bookKey);
|
||||
if (progress && viewRef.current) {
|
||||
scrollToProgress(progress);
|
||||
observer.disconnect();
|
||||
const updateHeight = () => {
|
||||
if (containerRef.current) {
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const parentContainer = containerRef.current.closest('.scroll-container');
|
||||
if (parentContainer) {
|
||||
const parentRect = parentContainer.getBoundingClientRect();
|
||||
const availableHeight = parentRect.height - (rect.top - parentRect.top);
|
||||
setContainerHeight(Math.max(400, availableHeight));
|
||||
}
|
||||
}
|
||||
};
|
||||
updateHeight();
|
||||
window.addEventListener('resize', updateHeight);
|
||||
let resizeObserver: ResizeObserver | null = null;
|
||||
if (containerRef.current) {
|
||||
const parentContainer = containerRef.current.closest('.scroll-container');
|
||||
if (parentContainer) {
|
||||
resizeObserver = new ResizeObserver(updateHeight);
|
||||
resizeObserver.observe(parentContainer);
|
||||
}
|
||||
});
|
||||
|
||||
if (viewRef.current) {
|
||||
observer.observe(viewRef.current, { childList: true, subtree: true });
|
||||
}
|
||||
return () => observer.disconnect();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [viewRef.current]);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateHeight);
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const activeHref = useMemo(() => progress?.sectionHref || null, [progress]);
|
||||
const flatItems = useFlattenedTOC(toc, expandedItems);
|
||||
|
||||
const handleToggleExpand = useCallback((item: TOCItem) => {
|
||||
const href = item.href || '';
|
||||
setExpandedItems((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
if (newSet.has(href)) {
|
||||
newSet.delete(href);
|
||||
} else {
|
||||
newSet.add(href);
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleItemClick = useCallback(
|
||||
(item: TOCItem) => {
|
||||
eventDispatcher.dispatch('navigate', { bookKey, href: item.href });
|
||||
if (item.href) {
|
||||
getView(bookKey)?.goTo(item.href);
|
||||
}
|
||||
},
|
||||
[bookKey, getView],
|
||||
);
|
||||
|
||||
const expandParents = useCallback((toc: TOCItem[], href: string) => {
|
||||
const parentPath = findParentPath(toc, href).map((item) => item.href);
|
||||
const parentHrefs = parentPath.filter(Boolean) as string[];
|
||||
setExpandedItems(new Set(parentHrefs));
|
||||
}, []);
|
||||
|
||||
const scrollToActiveItem = useCallback(() => {
|
||||
if (!activeHref) return;
|
||||
|
||||
if (vitualListRef.current) {
|
||||
const activeIndex = flatItems.findIndex((flatItem) => flatItem.item.href === activeHref);
|
||||
if (activeIndex !== -1) {
|
||||
vitualListRef.current.scrollToItem(activeIndex, 'center');
|
||||
}
|
||||
}
|
||||
|
||||
if (staticListRef.current) {
|
||||
const hrefMd5 = activeHref ? getContentMd5(activeHref) : '';
|
||||
const activeItem = staticListRef.current?.querySelector(`[data-href="${hrefMd5}"]`);
|
||||
if (activeItem) {
|
||||
const rect = activeItem.getBoundingClientRect();
|
||||
const isVisible = rect.top >= 0 && rect.bottom <= window.innerHeight;
|
||||
if (!isVisible) {
|
||||
(activeItem as HTMLElement).scrollIntoView({ behavior: 'instant', block: 'center' });
|
||||
}
|
||||
(activeItem as HTMLElement).setAttribute('aria-current', 'page');
|
||||
}
|
||||
}
|
||||
}, [activeHref, flatItems]);
|
||||
|
||||
const virtualItemSize = useMemo(() => {
|
||||
return window.innerWidth >= 640 && !viewSettings?.translationEnabled ? 37 : 57;
|
||||
}, [viewSettings]);
|
||||
|
||||
const virtualListData = useMemo(
|
||||
() => ({
|
||||
flatItems,
|
||||
itemSize: virtualItemSize,
|
||||
bookKey,
|
||||
activeHref,
|
||||
onToggleExpand: handleToggleExpand,
|
||||
onItemClick: handleItemClick,
|
||||
}),
|
||||
[flatItems, virtualItemSize, bookKey, activeHref, handleToggleExpand, handleItemClick],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!progress || eventDispatcher.dispatchSync('tts-is-speaking')) return;
|
||||
if (sideBarBookKey !== bookKey) return;
|
||||
if (!isSideBarVisible) return;
|
||||
|
||||
const { sectionHref: currentHref } = progress;
|
||||
if (currentHref) {
|
||||
expandParents(toc, currentHref);
|
||||
}
|
||||
scrollToProgress(progress);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [toc, progress, sideBarBookKey, isSideBarVisible]);
|
||||
}, [toc, progress, sideBarBookKey, isSideBarVisible, bookKey, expandParents]);
|
||||
|
||||
useEffect(() => {
|
||||
if (flatItems.length > 0) {
|
||||
setTimeout(scrollToActiveItem, 0);
|
||||
}
|
||||
}, [flatItems, scrollToActiveItem]);
|
||||
|
||||
return (
|
||||
<div className='rounded pt-2'>
|
||||
<ul role='tree' ref={viewRef} className='pe-4 ps-2 sm:pe-2'>
|
||||
{toc &&
|
||||
toc.map((item, index) => (
|
||||
<TOCItemView
|
||||
<div className='rounded' ref={containerRef}>
|
||||
{flatItems.length > 256 ? (
|
||||
<VirtualList
|
||||
ref={vitualListRef}
|
||||
width='100%'
|
||||
height={containerHeight}
|
||||
itemCount={flatItems.length}
|
||||
itemSize={virtualItemSize}
|
||||
itemData={virtualListData}
|
||||
overscanCount={20}
|
||||
>
|
||||
{VirtualListRow}
|
||||
</VirtualList>
|
||||
) : (
|
||||
<div className='pt-2' ref={staticListRef}>
|
||||
{flatItems.map((flatItem, index) => (
|
||||
<StaticListRow
|
||||
key={`static-row-${index}`}
|
||||
bookKey={bookKey}
|
||||
key={`${index}-${item.href}`}
|
||||
item={item}
|
||||
depth={0}
|
||||
expandedItems={expandedItems}
|
||||
flatItem={flatItem}
|
||||
activeHref={activeHref}
|
||||
onToggleExpand={handleToggleExpand}
|
||||
onItemClick={handleItemClick}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-1
Submodule packages/foliate-js updated: 19c6744d4b...d5286aeccf
Generated
+37
-13
@@ -161,9 +161,12 @@ importers:
|
||||
react-responsive:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0(react@19.0.0)
|
||||
react-window:
|
||||
specifier: ^1.8.11
|
||||
version: 1.8.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
semver:
|
||||
specifier: ^7.7.1
|
||||
version: 7.7.1
|
||||
version: 7.7.2
|
||||
tinycolor2:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0
|
||||
@@ -195,6 +198,9 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: 18.3.1
|
||||
version: 18.3.1
|
||||
'@types/react-window':
|
||||
specifier: ^1.8.8
|
||||
version: 1.8.8
|
||||
'@types/semver':
|
||||
specifier: ^7.7.0
|
||||
version: 7.7.0
|
||||
@@ -2751,6 +2757,9 @@ packages:
|
||||
'@types/react-dom@18.3.1':
|
||||
resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==}
|
||||
|
||||
'@types/react-window@1.8.8':
|
||||
resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==}
|
||||
|
||||
'@types/react@18.3.12':
|
||||
resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
|
||||
|
||||
@@ -4470,6 +4479,9 @@ packages:
|
||||
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
memoize-one@5.2.1:
|
||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
||||
|
||||
merge-descriptors@2.0.0:
|
||||
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -5076,6 +5088,13 @@ packages:
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
|
||||
react-window@1.8.11:
|
||||
resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==}
|
||||
engines: {node: '>8.0.0'}
|
||||
peerDependencies:
|
||||
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
react@18.3.1:
|
||||
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -5229,11 +5248,6 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.1:
|
||||
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.2:
|
||||
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -9529,6 +9543,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/react': 18.3.12
|
||||
|
||||
'@types/react-window@1.8.8':
|
||||
dependencies:
|
||||
'@types/react': 18.3.12
|
||||
|
||||
'@types/react@18.3.12':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.13
|
||||
@@ -9616,7 +9634,7 @@ snapshots:
|
||||
fast-glob: 3.3.2
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
semver: 7.7.1
|
||||
semver: 7.7.2
|
||||
ts-api-utils: 1.4.3(typescript@5.7.2)
|
||||
optionalDependencies:
|
||||
typescript: 5.7.2
|
||||
@@ -11182,7 +11200,7 @@ snapshots:
|
||||
|
||||
is-bun-module@1.3.0:
|
||||
dependencies:
|
||||
semver: 7.7.1
|
||||
semver: 7.7.2
|
||||
|
||||
is-callable@1.2.7: {}
|
||||
|
||||
@@ -11456,6 +11474,8 @@ snapshots:
|
||||
|
||||
media-typer@1.1.0: {}
|
||||
|
||||
memoize-one@5.2.1: {}
|
||||
|
||||
merge-descriptors@2.0.0: {}
|
||||
|
||||
merge-stream@2.0.0: {}
|
||||
@@ -11965,6 +11985,13 @@ snapshots:
|
||||
react: 19.0.0
|
||||
shallow-equal: 3.1.0
|
||||
|
||||
react-window@1.8.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.0
|
||||
memoize-one: 5.2.1
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
react@18.3.1:
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
@@ -12149,10 +12176,7 @@ snapshots:
|
||||
|
||||
semver@7.6.3: {}
|
||||
|
||||
semver@7.7.1: {}
|
||||
|
||||
semver@7.7.2:
|
||||
optional: true
|
||||
semver@7.7.2: {}
|
||||
|
||||
send@1.1.0:
|
||||
dependencies:
|
||||
@@ -12214,7 +12238,7 @@ snapshots:
|
||||
dependencies:
|
||||
color: 4.2.3
|
||||
detect-libc: 2.0.3
|
||||
semver: 7.7.1
|
||||
semver: 7.7.2
|
||||
optionalDependencies:
|
||||
'@img/sharp-darwin-arm64': 0.33.5
|
||||
'@img/sharp-darwin-x64': 0.33.5
|
||||
|
||||
Reference in New Issue
Block a user