-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathreadOnlyTooltip.ts
More file actions
123 lines (105 loc) · 4.07 KB
/
readOnlyTooltip.ts
File metadata and controls
123 lines (105 loc) · 4.07 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
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
/*
* Copyright (c) 2024. Devtron Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EditorView, Extension, showTooltip, StateEffect, StateField, Tooltip, ViewPlugin } from '@uiw/react-codemirror'
import { READ_ONLY_TOOLTIP_TIMEOUT } from '../CodeEditor.constants'
import { getReadOnlyElement } from '../utils'
/** Array of keys to be ignored on keypress */
const ignoreKeys = ['ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft', 'Enter', 'Escape']
// Effect to update the tooltip in the editor state
const updateTooltipEffect = StateEffect.define<Tooltip | null>()
// StateField to manage tooltip state
const tooltipField = StateField.define<Tooltip | null>({
// Initializes the tooltip state as null
create: () => null,
// Updates the tooltip state based on the dispatched effects
update: (value, tr) => {
const effect = tr.effects.find((_effect) => _effect.is(updateTooltipEffect))
if (effect) {
return effect.value
}
return value
},
// Provides the tooltip to the editor's UI
provide: (field) => showTooltip.from(field),
})
/**
* Creates a tooltip at the current cursor position.
* @param view - The editor view instance.
* @returns The tooltip object or null if no cursor position is available.
*/
const createTooltip = (view: EditorView): Tooltip => {
const cursorPos = view.state.selection.main.head
if (cursorPos === null) {
return null
}
// Get read-only element UI.
const dom = getReadOnlyElement()
dom.classList.add('dc__w-fit-content')
// Attach to body to measure its dimensions
document.body.appendChild(dom)
const offset = -(dom.getBoundingClientRect().width / 2)
document.body.removeChild(dom)
return {
pos: cursorPos,
// Display the tooltip above the cursor
above: true,
// Provide tooltip dom element and offset it's position to be in center.
create: () => ({ dom, offset: { x: offset, y: 4 }, overlap: true }),
}
}
// Plugin to show and remove tooltip on keypress
const keypressTooltipPlugin = ViewPlugin.fromClass(
class {
private timeoutId: ReturnType<typeof setTimeout> | number | null = null
constructor(public view: EditorView) {
this.view.dom.addEventListener('keydown', this.handleKeyPress)
}
destroy() {
this.view.dom.removeEventListener('keydown', this.handleKeyPress)
if (this.timeoutId) {
clearTimeout(this.timeoutId)
}
}
handleKeyPress = (e: KeyboardEvent) => {
if (
!this.view.state.readOnly ||
ignoreKeys.includes(e.key) ||
e.metaKey ||
e.shiftKey ||
e.altKey ||
e.ctrlKey
) {
return
}
// Show tooltip
const tooltip = createTooltip(this.view)
this.view.dispatch({ effects: updateTooltipEffect.of(tooltip) })
// Reset the timer after every key press
if (this.timeoutId) {
clearTimeout(this.timeoutId)
}
// Remove tooltip after timeout time
this.timeoutId = setTimeout(() => {
this.view.dispatch({ effects: updateTooltipEffect.of(null) })
}, READ_ONLY_TOOLTIP_TIMEOUT)
}
},
)
/**
* The read-only tooltip extension for CodeMirror. \
* Displays a tooltip at the cursor position when the editor is read-only and key is pressed.
*/
export const readOnlyTooltip: Extension = [tooltipField, keypressTooltipPlugin]