-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathremote-folder-path.ts
More file actions
81 lines (77 loc) · 2.61 KB
/
Copy pathremote-folder-path.ts
File metadata and controls
81 lines (77 loc) · 2.61 KB
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
/** Parent directory of a remote POSIX path (`/` is its own parent). */
export function parentRemotePath(path: string): string {
if (path === '/' || path === '') return '/'
const trimmed = path.replace(/\/+$/, '')
const idx = trimmed.lastIndexOf('/')
return idx <= 0 ? '/' : trimmed.slice(0, idx)
}
export interface RemotePathSegment {
/** Display label (`/` for root, otherwise the directory name). */
label: string
/** Absolute remote path for this crumb. */
path: string
}
/** Breadcrumb segments for a remote POSIX path, always starting at `/`. */
export function remotePathSegments(path: string): RemotePathSegment[] {
const normalized =
!path || path === '/'
? '/'
: path.startsWith('/')
? path.replace(/\/+$/, '') || '/'
: `/${path}`
const segments: RemotePathSegment[] = [{ label: '/', path: '/' }]
if (normalized === '/') return segments
let acc = ''
for (const part of normalized.split('/').filter(Boolean)) {
acc += `/${part}`
segments.push({ label: part, path: acc })
}
return segments
}
/**
* Whether to render a `/` separator after this crumb. Root's label is already
* `/`, so a separator after it reads as the doubled `/ / usr` path.
*/
export function remotePathShowsSeparatorAfter(segment: RemotePathSegment): boolean {
return segment.path !== '/'
}
/**
* Fill a breadcrumb nav for a remote POSIX path. `onNavigate` is called for
* non-current crumbs; separators are omitted after the root `/` crumb.
*/
export function fillRemotePathBreadcrumbs(
nav: HTMLElement,
path: string,
onNavigate: (path: string) => void,
): void {
nav.replaceChildren()
const segments = remotePathSegments(path)
for (let i = 0; i < segments.length; i += 1) {
const segment = segments[i]
if (!segment) continue
const isCurrent = i === segments.length - 1
if (isCurrent) {
const current = document.createElement('span')
current.className = 'remote-folder-crumb remote-folder-crumb-current'
current.textContent = segment.label
nav.append(current)
continue
}
const crumb = document.createElement('button')
crumb.type = 'button'
crumb.className = 'remote-folder-crumb'
crumb.setAttribute('aria-label', `Go to ${segment.path}`)
crumb.textContent = segment.label
crumb.addEventListener('click', () => {
onNavigate(segment.path)
})
nav.append(crumb)
if (remotePathShowsSeparatorAfter(segment)) {
const sep = document.createElement('span')
sep.className = 'remote-folder-crumb-sep'
sep.setAttribute('aria-hidden', 'true')
sep.textContent = '/'
nav.append(sep)
}
}
}