v0.1.3 ff61cc9

tooltip.svelte RawHistoryFolder

1<script lang="ts">
2  type $$Props = {
3    data: {
4      text: string,
5      direction?: 'top' | 'bottom' | 'left' | 'right'
6    }
7  };
8
9  let { children, data } = $props();
10  
11  // Derive values from data with defaults
12  let text = $derived(data.text);
13  let direction = $derived(data.direction ?? 'top');
14
15  let wrapper: HTMLDivElement;
16  let tooltipDiv: HTMLDivElement | null = null;
17  let coords = { x: 0, y: 0 };
18
19  function updateCoords() {
20    if (wrapper) {
21      const rect = wrapper.getBoundingClientRect();
22      if (direction === 'top') {
23        coords = { x: rect.left + rect.width / 2, y: rect.top - 8 };
24      } else if (direction === 'bottom') {
25        coords = { x: rect.left + rect.width / 2, y: rect.bottom + 8 };
26      } else if (direction === 'left') {
27        coords = { x: rect.left - 8, y: rect.top + rect.height / 2 };
28      } else if (direction === 'right') {
29        coords = { x: rect.right + 8, y: rect.top + rect.height / 2 };
30      }
31    }
32  }
33
34  function showTooltip() {
35    updateCoords();
36    tooltipDiv = document.createElement('div');
37    tooltipDiv.className = `tooltip ${direction}`;
38    tooltipDiv.style.position = 'fixed';
39    tooltipDiv.style.zIndex = '1000000';
40    tooltipDiv.textContent = text;
41    document.body.appendChild(tooltipDiv);
42
43    const tooltipRect = tooltipDiv.getBoundingClientRect();
44    let finalLeft = coords.x;
45    let finalTop = coords.y;
46    if (direction === 'top') {
47      finalLeft = coords.x - tooltipRect.width / 2;
48      finalTop = coords.y - tooltipRect.height;
49    } else if (direction === 'bottom') {
50      finalLeft = coords.x - tooltipRect.width / 2;
51      finalTop = coords.y;
52    } else if (direction === 'left') {
53      finalLeft = coords.x - tooltipRect.width;
54      finalTop = coords.y - tooltipRect.height / 2;
55    } else if (direction === 'right') {
56      finalLeft = coords.x;
57      finalTop = coords.y - tooltipRect.height / 2;
58    }
59
60    if (finalLeft < 0) {
61      finalLeft = 0;
62    } else if (finalLeft + tooltipRect.width > window.innerWidth) {
63      finalLeft = window.innerWidth - tooltipRect.width;
64    }
65    if (finalTop < 0) {
66      finalTop = 0;
67    } else if (finalTop + tooltipRect.height > window.innerHeight) {
68      finalTop = window.innerHeight - tooltipRect.height;
69    }
70
71    tooltipDiv.style.left = `${finalLeft}px`;
72    tooltipDiv.style.top = `${finalTop}px`;
73  }
74
75  function hideTooltip() {
76    if (tooltipDiv && tooltipDiv.parentNode) {
77      tooltipDiv.parentNode.removeChild(tooltipDiv);
78      tooltipDiv = null;
79    }
80  }
81</script>
82
83<div 
84  class="tooltip-wrapper" 
85  bind:this={wrapper}
86  onmouseover={showTooltip}
87  onmouseout={hideTooltip}
88  onfocus={showTooltip}
89  onblur={hideTooltip}
90  role="tooltip"
91  tabindex="-1"
92>
93  {@render children()}
94</div>
95
96<style>
97  .tooltip-wrapper {
98    display: inline-block;
99  }
100  /* Use :global for styles that need to apply to elements outside the component */
101  :global(.tooltip) {
102    white-space: nowrap;
103    padding: 0.5rem;
104    background: #ECE9D8;
105    color: #000;
106    border: 1px solid #003C74;
107    box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
108    border-radius: 2px;
109    font-size: 0.875rem;
110    font-family: 'Microsoft Sans Serif', 'Segoe UI', Arial, Tahoma, sans-serif;
111  }
112
113  /* Direction-specific styles for the tooltip arrow */
114  :global(.tooltip.top)::before {
115    content: '';
116    position: absolute;
117    bottom: -6px;
118    left: 50%;
119    transform: translateX(-50%);
120    border-left: 6px solid transparent;
121    border-right: 6px solid transparent;
122    border-top: 6px solid #003C74;
123  }
124
125  :global(.tooltip.bottom)::before {
126    content: '';
127    position: absolute;
128    top: -6px;
129    left: 50%;
130    transform: translateX(-50%);
131    border-left: 6px solid transparent;
132    border-right: 6px solid transparent;
133    border-bottom: 6px solid #003C74;
134  }
135
136  :global(.tooltip.left)::before {
137    content: '';
138    position: absolute;
139    right: -6px;
140    top: 50%;
141    transform: translateY(-50%);
142    border-top: 6px solid transparent;
143    border-bottom: 6px solid transparent;
144    border-left: 6px solid #003C74;
145  }
146
147  :global(.tooltip.right)::before {
148    content: '';
149    position: absolute;
150    left: -6px;
151    top: 50%;
152    transform: translateY(-50%);
153    border-top: 6px solid transparent;
154    border-bottom: 6px solid transparent;
155    border-right: 6px solid #003C74;
156  }
157</style>

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close