| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- import React, { useState, useEffect, useRef, useCallback } from 'react';
- import { View, ScrollView } from '@tarojs/components';
- import './CategorySidebar.css';
- export interface CategorySidebarProps {
- /** 当前选中的分类索引 */
- activeKey?: number;
- /** 自定义类名 */
- className?: string;
- /** 选中项改变时的回调 */
- onChange?: (index: number) => void;
- /** 子组件 */
- children?: React.ReactNode;
- }
- interface CategorySidebarContextType {
- registerItem: (item: any) => number;
- unregisterItem: (item: any) => void;
- setActive: (index: number) => Promise<void>;
- activeKey: number;
- }
- export const CategorySidebarContext = React.createContext<CategorySidebarContextType | null>(null);
- const CategorySidebar: React.FC<CategorySidebarProps> = (props) => {
- const { activeKey = 0, className = '', onChange, children } = props;
- const [currentActive, setCurrentActive] = useState<number>(activeKey);
- const [childrenList, setChildrenList] = useState<any[]>([]);
- const [topRightRadiusItemIndexs, setTopRightRadiusItemIndexs] = useState<number[]>([]);
- const [bottomRightRadiusItemIndexs, setBottomRightRadiusItemIndexs] = useState<number[]>([]);
- const childrenRef = useRef<any[]>([]);
- const currentActiveRef = useRef<number>(currentActive);
- const topRightRadiusItemIndexsRef = useRef<number[]>([]);
- const bottomRightRadiusItemIndexsRef = useRef<number[]>([]);
- // 更新 ref 值
- useEffect(() => {
- childrenRef.current = childrenList;
- currentActiveRef.current = currentActive;
- topRightRadiusItemIndexsRef.current = topRightRadiusItemIndexs;
- bottomRightRadiusItemIndexsRef.current = bottomRightRadiusItemIndexs;
- }, [childrenList, currentActive, topRightRadiusItemIndexs, bottomRightRadiusItemIndexs]);
- // 注册子组件
- const registerItem = useCallback((item: any) => {
- const currentLength = childrenRef.current.length;
- const itemIndex = currentLength;
- setChildrenList(prev => {
- const newList = [...prev, item];
- childrenRef.current = newList;
- //console.debug('CategorySidebar registerItem:', { itemIndex, totalItems: newList.length });
- return newList;
- });
- return itemIndex;
- }, []);
- // 注销子组件
- const unregisterItem = useCallback((item: any) => {
- setChildrenList(prev => {
- const newList = prev.filter(i => i !== item);
- childrenRef.current = newList;
- return newList;
- });
- }, []);
- // 计算顶部圆角项索引
- const getTopRightRadiusItemIndexs = useCallback((activeKey: number, children: any[]) => {
- const { length } = children;
- if (activeKey !== 0 && activeKey < length - 1) return [0, activeKey + 1];
- if (activeKey !== 0) return [0];
- if (activeKey < length - 1) return [activeKey + 1];
- return [];
- }, []);
- // 计算底部圆角项索引
- const getBottomRightRadiusItemIndexs = useCallback((activeKey: number) => {
- if (activeKey !== 0) return [activeKey - 1];
- return [];
- }, []);
- // 设置选中状态
- const setActive = useCallback(async (activeKey: number, isChildrenChange = false) => {
- const children = childrenRef.current;
- const currentActive = currentActiveRef.current;
- const preTopRightRadiusItemIndexs = topRightRadiusItemIndexsRef.current;
- const preBottomRightRadiusItemIndexs = bottomRightRadiusItemIndexsRef.current;
- if (!children.length) {
- // console.debug('CategorySidebar setActive: no children available');
- return;
- }
- // 如果是子组件变化触发的,即使 activeKey 相同也要重新设置样式
- // 或者如果是用户点击同一个item,也需要重新设置样式
- if (activeKey === currentActive && !isChildrenChange) {
- // 即使点击同一个item,也要确保样式正确应用
- //console.debug('CategorySidebar setActive: same item clicked, ensuring styles');
- }
- // console.debug('CategorySidebar setActive:', { activeKey, currentActive, isChildrenChange, childrenCount: children.length });
- // 确保 activeKey 在有效范围内
- if (activeKey < 0 || activeKey >= children.length) {
- // console.debug('CategorySidebar setActive: invalid activeKey, using 0');
- setCurrentActive(0);
- return;
- }
- setCurrentActive(activeKey);
- const newTopRightRadiusItemIndexs = getTopRightRadiusItemIndexs(activeKey, children);
- const newBottomRightRadiusItemIndexs = getBottomRightRadiusItemIndexs(activeKey);
- // console.debug('CategorySidebar setActive - radius indexes:', {
- // top: newTopRightRadiusItemIndexs,
- // bottom: newBottomRightRadiusItemIndexs
- // });
- setTopRightRadiusItemIndexs(newTopRightRadiusItemIndexs);
- setBottomRightRadiusItemIndexs(newBottomRightRadiusItemIndexs);
- const promises: Promise<void>[] = [];
- // 移除旧的圆角效果
- preTopRightRadiusItemIndexs.forEach((item) => {
- if (children[item]) {
- promises.push(children[item].setTopRightRadius(false));
- }
- });
- preBottomRightRadiusItemIndexs.forEach((item) => {
- if (children[item]) {
- promises.push(children[item].setBottomRightRadius(false));
- }
- });
- // 应用新的圆角效果
- newTopRightRadiusItemIndexs.forEach((item) => {
- if (children[item]) {
- promises.push(children[item].setTopRightRadius(true));
- }
- });
- newBottomRightRadiusItemIndexs.forEach((item) => {
- if (children[item]) {
- promises.push(children[item].setBottomRightRadius(true));
- }
- });
- await Promise.all(promises);
- // 触发 onChange 回调
- if (onChange && !isChildrenChange) {
- onChange(activeKey);
- }
- }, [getTopRightRadiusItemIndexs, getBottomRightRadiusItemIndexs, onChange]);
- // 监听 activeKey 变化
- useEffect(() => {
- if (activeKey !== currentActive) {
- setActive(activeKey);
- }
- }, [activeKey, setActive]);
- // 组件挂载时设置初始选中状态
- useEffect(() => {
- if (childrenList.length > 0 && currentActive === activeKey) {
- setActive(activeKey, true);
- }
- }, [childrenList, setActive, currentActive, activeKey]);
- // 子组件变化时重新设置选中状态
- useEffect(() => {
- if (childrenList.length > 0) {
- setActive(currentActive, true);
- }
- }, [childrenList, setActive]);
- const contextValue: CategorySidebarContextType = {
- registerItem,
- unregisterItem,
- setActive,
- activeKey: currentActive,
- };
- return (
- <CategorySidebarContext.Provider value={contextValue}>
- <ScrollView
- className={`category-sidebar ${className}`}
- scrollY
- scrollWithAnimation
- >
- <View className="category-sidebar__content">
- {children}
- </View>
- </ScrollView>
- </CategorySidebarContext.Provider>
- );
- };
- export default CategorySidebar;
|