|
|
@@ -1,5 +1,4 @@
|
|
|
-import React, { useRef } from 'react';
|
|
|
-import { useScrollIndicator, useTouchScroll } from '../hooks/useScrollIndicator';
|
|
|
+import React from 'react';
|
|
|
import { INK_COLORS } from '../styles/colors';
|
|
|
import { FONT_STYLES } from '../styles/typography';
|
|
|
|
|
|
@@ -15,93 +14,77 @@ interface CategoryScrollProps {
|
|
|
}
|
|
|
|
|
|
export const CategoryScroll: React.FC<CategoryScrollProps> = ({ categories, onNavigate }) => {
|
|
|
- const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
|
- const { showIndicator } = useScrollIndicator({
|
|
|
- containerRef: scrollContainerRef,
|
|
|
- threshold: 0.1
|
|
|
- });
|
|
|
- const { handleTouchStart, handleTouchEnd, handleTouchMove } = useTouchScroll();
|
|
|
-
|
|
|
if (categories.length === 0) return null;
|
|
|
|
|
|
+ // 2行布局,每行4个图标,总共显示8个图标
|
|
|
const itemsPerRow = 4;
|
|
|
const totalRows = 2;
|
|
|
- const visibleItems = Math.min(itemsPerRow * totalRows, categories.length);
|
|
|
+ const maxVisibleItems = itemsPerRow * totalRows;
|
|
|
+ const visibleCategories = categories.slice(0, maxVisibleItems);
|
|
|
|
|
|
return (
|
|
|
- <div className="relative">
|
|
|
- <div
|
|
|
- ref={scrollContainerRef}
|
|
|
- className="overflow-x-auto pb-2 scroll-container"
|
|
|
- style={{
|
|
|
- WebkitOverflowScrolling: 'touch',
|
|
|
- height: '200px',
|
|
|
- cursor: 'grab'
|
|
|
- }}
|
|
|
- onTouchStart={handleTouchStart}
|
|
|
- onTouchEnd={handleTouchEnd}
|
|
|
- onTouchMove={handleTouchMove}
|
|
|
- >
|
|
|
- <div
|
|
|
- className="grid gap-3"
|
|
|
- style={{
|
|
|
- gridTemplateColumns: `repeat(${Math.max(8, categories.length)}, 1fr)`,
|
|
|
- gridTemplateRows: 'repeat(2, 1fr)',
|
|
|
- minWidth: `${Math.max(categories.length * 82, 340)}px`,
|
|
|
- width: 'max-content',
|
|
|
- height: '100%',
|
|
|
- paddingRight: '30px'
|
|
|
- }}
|
|
|
- >
|
|
|
- {categories.map((category, index) => (
|
|
|
- <button
|
|
|
- key={index}
|
|
|
- onClick={() => onNavigate(category.path || '/')}
|
|
|
- className="category-item flex flex-col items-center justify-center p-2 rounded-xl transition-all duration-300 hover:shadow-lg backdrop-blur-sm cursor-pointer touch-feedback"
|
|
|
- style={{
|
|
|
- backgroundColor: 'rgba(255,255,255,0.7)',
|
|
|
- border: `1px solid ${INK_COLORS.ink.medium}`,
|
|
|
- color: INK_COLORS.text.primary,
|
|
|
- width: '75px',
|
|
|
- height: '85px'
|
|
|
- }}
|
|
|
+ <div className="w-full">
|
|
|
+ {/* 2行固定网格布局 */}
|
|
|
+ <div className="grid grid-cols-4 gap-3">
|
|
|
+ {visibleCategories.map((category, index) => (
|
|
|
+ <button
|
|
|
+ key={index}
|
|
|
+ onClick={() => onNavigate(category.path || '/')}
|
|
|
+ className="category-item flex flex-col items-center justify-center p-2 rounded-xl transition-all duration-300 hover:shadow-lg backdrop-blur-sm cursor-pointer touch-feedback"
|
|
|
+ style={{
|
|
|
+ backgroundColor: 'rgba(255,255,255,0.7)',
|
|
|
+ border: `1px solid ${INK_COLORS.ink.medium}`,
|
|
|
+ color: INK_COLORS.text.primary,
|
|
|
+ width: '100%',
|
|
|
+ aspectRatio: '1/1.2',
|
|
|
+ maxWidth: '80px'
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ className="w-12 h-12 rounded-full flex items-center justify-center shadow-sm mb-1"
|
|
|
+ style={{ backgroundColor: INK_COLORS.accent.blue, color: 'white' }}
|
|
|
>
|
|
|
- <div
|
|
|
- className="w-12 h-12 rounded-full flex items-center justify-center shadow-sm mb-1"
|
|
|
- style={{ backgroundColor: INK_COLORS.accent.blue, color: 'white' }}
|
|
|
- >
|
|
|
- <img
|
|
|
- src={category.image || '/images/placeholder.jpg'}
|
|
|
- alt={category.name}
|
|
|
- className="w-7 h-7 object-cover rounded-full"
|
|
|
- onError={(e) => {
|
|
|
- (e.target as HTMLImageElement).style.display = 'none';
|
|
|
- (e.target as HTMLImageElement).parentElement!.innerHTML = '📱';
|
|
|
- }}
|
|
|
- />
|
|
|
- </div>
|
|
|
- <span
|
|
|
- className={`${FONT_STYLES.caption} text-xs text-center line-clamp-2`}
|
|
|
- style={{ fontSize: '11px', fontWeight: 500 }}
|
|
|
- >
|
|
|
- {category.name}
|
|
|
- </span>
|
|
|
- </button>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 智能滚动指示器 */}
|
|
|
- {showIndicator && categories.length > visibleItems && (
|
|
|
- <div className="absolute right-0 top-0 bottom-0 flex items-center pointer-events-none">
|
|
|
- <div className="w-8 h-full bg-gradient-to-l from-ink-light via-ink-light/80 to-transparent"></div>
|
|
|
- <div className="absolute right-1 top-1/2 -translate-y-1/2">
|
|
|
- <div className="w-6 h-6 rounded-full bg-white shadow-lg flex items-center justify-center scroll-indicator">
|
|
|
- <svg className="w-3 h-3 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
|
- </svg>
|
|
|
+ <img
|
|
|
+ src={category.image || '/images/placeholder.jpg'}
|
|
|
+ alt={category.name}
|
|
|
+ className="w-7 h-7 object-cover rounded-full"
|
|
|
+ onError={(e) => {
|
|
|
+ (e.target as HTMLImageElement).style.display = 'none';
|
|
|
+ (e.target as HTMLImageElement).parentElement!.innerHTML = '📱';
|
|
|
+ }}
|
|
|
+ />
|
|
|
</div>
|
|
|
+ <span
|
|
|
+ className={`${FONT_STYLES.caption} text-xs text-center line-clamp-2`}
|
|
|
+ style={{ fontSize: '11px', fontWeight: 500 }}
|
|
|
+ >
|
|
|
+ {category.name}
|
|
|
+ </span>
|
|
|
+ </button>
|
|
|
+ ))}
|
|
|
+
|
|
|
+ {/* 如果图标不足8个,填充空白占位符 */}
|
|
|
+ {Array.from({ length: Math.max(0, maxVisibleItems - visibleCategories.length) }).map((_, index) => (
|
|
|
+ <div
|
|
|
+ key={`placeholder-${index}`}
|
|
|
+ className="flex flex-col items-center justify-center p-2"
|
|
|
+ style={{ opacity: 0, pointerEvents: 'none' }}
|
|
|
+ >
|
|
|
+ <div className="w-12 h-12 rounded-full"></div>
|
|
|
+ <span className="text-xs text-center mt-1"></span>
|
|
|
</div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 如果分类超过8个,显示提示 */}
|
|
|
+ {categories.length > maxVisibleItems && (
|
|
|
+ <div className="text-center mt-2">
|
|
|
+ <span
|
|
|
+ className={`${FONT_STYLES.caption} text-xs`}
|
|
|
+ style={{ color: INK_COLORS.text.light }}
|
|
|
+ >
|
|
|
+ 还有 {categories.length - maxVisibleItems} 个分类
|
|
|
+ </span>
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|