File size: 4,118 Bytes
494c9e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
 * 路径导航栏组件
 * 显示面包屑导航,支持点击跳转
 */
import * as d3 from 'd3';
import { tr } from '../lang/i18n-lite';

export type PathNavigator = {
    update: (path: string) => void;
};

export function createPathNavigator(
    container: d3.Selection<HTMLElement, any, any, any>,
    currentPath: string,
    onPathChange: (path: string) => void,
    onCreateFolder?: () => void
): PathNavigator {
    const navWrapper = container.append('div')
        .attr('class', 'demo-path-nav-wrapper')
        .style('display', 'flex')
        .style('align-items', 'center')
        .style('justify-content', 'space-between')
        .style('gap', '10px');

    const navContainer = navWrapper.append('div')
        .attr('class', 'demo-path-navigator')
        .style('flex', '1')
        .style('font-size', '12px')
        .style('color', 'var(--text-color)');

    // 新建文件夹按钮
    if (onCreateFolder) {
        const createBtn = navWrapper.append('button')
            .attr('class', 'refresh-btn')
            .attr('title', tr('New folder'))
            .style('flex-shrink', '0')
            .text('+')
            .on('click', function() {
                onCreateFolder();
            });
    }

    const update = (path: string) => {
        navContainer.selectAll('*').remove();

        // 解析路径段
        const pathSegments: Array<{ name: string; path: string; isRoot?: boolean }> = [];
        
        // 根目录
        pathSegments.push({ name: '', path: '/', isRoot: true });

        if (path && path !== '/') {
            // 分割路径(去掉开头的 "/")
            const segments = path.split('/').filter(s => s);
            let currentFullPath = '/';
            
            segments.forEach(segment => {
                // 统一使用 "/" 开头的路径格式
                currentFullPath = currentFullPath === '/' ? `/${segment}` : `${currentFullPath}/${segment}`;
                pathSegments.push({
                    name: decodeURIComponent(segment),
                    path: currentFullPath
                });
            });
        }

        // 渲染路径段
        const pathItems = navContainer.selectAll('.path-segment')
            .data(pathSegments)
            .join('span')
            .attr('class', 'path-segment')
            .style('cursor', 'pointer')
            .style('color', 'var(--text-color)')
            .style('opacity', '0.7')
            .style('transition', 'opacity 0.2s')
            .on('mouseenter', function() {
                d3.select(this).style('opacity', '1');
            })
            .on('mouseleave', function() {
                d3.select(this).style('opacity', '0.7');
            })
            .on('click', function(_, d) {
                onPathChange(d.path);
            })
            .each(function(d) {
                const seg = d3.select(this);
                if (d.isRoot) {
                    seg
                        .attr('title', tr('/(Root)'))
                        .attr('aria-label', tr('/(Root)'))
                        .text('⌂');
                    return;
                }
                seg.text(d.name);
            });

        // 添加分隔符
        const separators = navContainer.selectAll('.path-separator')
            .data(pathSegments.slice(0, -1))
            .join('span')
            .attr('class', 'path-separator')
            .style('margin', '0 6px')
            .style('opacity', '0.5')
            .text(' > ');

        // 重新排序:将分隔符插入到正确位置
        pathItems.each(function(d, i) {
            if (i > 0) {
                const separator = separators.filter((_, idx) => idx === i - 1);
                const separatorNode = separator.node() as HTMLElement | null;
                const thisNode = this as HTMLElement;
                if (separatorNode && thisNode.parentNode) {
                    thisNode.parentNode.insertBefore(separatorNode, thisNode);
                }
            }
        });
    };

    // 初始渲染
    update(currentPath);

    return { update };
}