Skip to content

Commit dbbd7d2

Browse files
committed
feat: enhance editor functionality with searchable language selector and formatting options
- Introduced a new SearchableLanguageSelector component for improved language selection with search capabilities. - Updated Editor component to include a format button with tooltip for document formatting. - Enhanced styling for editor toolbar and language selector for better user experience. - Added responsive design elements to prevent overflow and improve layout consistency.
1 parent e43f771 commit dbbd7d2

File tree

3 files changed

+473
-6
lines changed

3 files changed

+473
-6
lines changed

src/frontend/src/pad/editors/Editor.scss

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,51 @@
11
.editor {
22
&__wrapper {
3-
display: flex;
4-
flex-direction: column;
3+
position: relative; /* For absolute positioning of toolbar */
54
height: 100%;
65
width: 100%;
6+
overflow: hidden; /* Prevent overflow issues */
7+
border-bottom-left-radius: 20px;
8+
border-bottom-right-radius: 20px;
79
}
810

911
&__toolbar {
1012
display: flex;
1113
justify-content: right;
1214
padding: 8px;
15+
position: absolute; /* Position at bottom */
16+
bottom: 0;
17+
left: 0;
18+
right: 0;
19+
z-index: 10; /* Keep toolbar above other elements */
20+
background-color: #191919; /* Match editor background */
21+
border-top: 1px solid #3c3c3c;
22+
gap: 8px; /* Add spacing between toolbar items */
23+
}
24+
25+
&__format-button {
26+
background-color: #252526;
27+
color: #cccccc;
28+
border: 1px solid #3c3c3c;
29+
border-radius: 7px;
30+
padding: 4px 8px;
31+
cursor: pointer;
32+
display: flex;
33+
align-items: center;
34+
justify-content: center;
35+
36+
&:hover {
37+
background-color: #2a2d2e;
38+
color: #ffffff;
39+
}
40+
41+
&:focus {
42+
outline: none;
43+
border-color: #007fd4;
44+
}
1345
}
1446

1547
&__language-selector {
48+
display: flex;
1649
margin-right: 10px;
1750
}
1851

@@ -35,8 +68,106 @@
3568
color: #cccccc;
3669
}
3770
}
71+
72+
&__searchable-language-container {
73+
display: flex;
74+
align-items: center;
75+
background-color: #252526;
76+
border: 1px solid #3c3c3c;
77+
border-radius: 7px;
78+
max-width: 150px;
79+
position: relative;
80+
81+
&:focus-within {
82+
border-color: #007fd4;
83+
}
84+
}
85+
86+
&__searchable-language-input {
87+
background-color: transparent;
88+
color: #cccccc;
89+
border: none;
90+
padding: 4px 8px;
91+
font-size: 12px;
92+
width: 100%;
93+
94+
&:focus {
95+
outline: none;
96+
}
97+
98+
&::placeholder {
99+
color: #cccccc;
100+
opacity: 0.8;
101+
}
102+
}
103+
104+
&__searchable-language-toggle {
105+
background: none;
106+
border: none;
107+
color: #cccccc;
108+
cursor: pointer;
109+
display: flex;
110+
align-items: center;
111+
justify-content: center;
112+
padding: 0 6px;
113+
transition: transform 0.2s ease;
114+
115+
&:hover {
116+
color: #ffffff;
117+
}
118+
119+
&:focus {
120+
outline: none;
121+
}
122+
}
123+
124+
&__searchable-language-dropdown {
125+
position: absolute;
126+
bottom: 100%; /* Position above instead of below */
127+
left: 0;
128+
right: 0;
129+
max-height: 200px;
130+
overflow-y: auto;
131+
background-color: #252526;
132+
border: 1px solid #3c3c3c;
133+
border-radius: 7px;
134+
margin-bottom: 4px; /* Margin at bottom instead of top */
135+
z-index: 20; /* Higher z-index to ensure it's above the toolbar */
136+
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.3); /* Shadow adjusted for upward direction */
137+
}
138+
139+
&__searchable-language-option {
140+
padding: 4px 8px;
141+
font-size: 12px;
142+
color: #cccccc;
143+
cursor: pointer;
144+
145+
&:hover {
146+
background-color: #2a2d2e;
147+
}
148+
149+
&--highlighted {
150+
background-color: #04395e;
151+
}
152+
153+
&--selected {
154+
color: #ffffff;
155+
font-weight: 500;
156+
}
157+
}
158+
159+
&__searchable-language-no-results {
160+
padding: 4px 8px;
161+
font-size: 12px;
162+
color: #cccccc;
163+
font-style: italic;
164+
text-align: center;
165+
}
38166

39167
&__container {
40-
flex: 1;
168+
height: 100%;
169+
width: 100%;
170+
padding-bottom: 40px; /* Make room for the toolbar */
171+
box-sizing: border-box;
41172
}
42173
}

src/frontend/src/pad/editors/Editor.tsx

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,35 @@
11
import React, { useRef, useState, useEffect, useCallback } from 'react';
22
import MonacoEditor from '@monaco-editor/react';
3-
import LanguageSelector from './LanguageSelector';
3+
import { Tooltip, updateTooltipPosition, getTooltipDiv } from '@atyrode/excalidraw';
4+
import SearchableLanguageSelector from './SearchableLanguageSelector';
45
import './Editor.scss';
56

7+
// Custom tooltip wrapper that positions the tooltip at the top
8+
const TopTooltip: React.FC<{label: string, children: React.ReactNode}> = ({ label, children }) => {
9+
const handlePointerEnter = (event: React.PointerEvent<HTMLDivElement>) => {
10+
const tooltip = getTooltipDiv();
11+
tooltip.classList.add("excalidraw-tooltip--visible");
12+
tooltip.textContent = label;
13+
14+
const itemRect = event.currentTarget.getBoundingClientRect();
15+
updateTooltipPosition(tooltip, itemRect, "top");
16+
};
17+
18+
const handlePointerLeave = () => {
19+
getTooltipDiv().classList.remove("excalidraw-tooltip--visible");
20+
};
21+
22+
return (
23+
<div
24+
className="excalidraw-tooltip-wrapper"
25+
onPointerEnter={handlePointerEnter}
26+
onPointerLeave={handlePointerLeave}
27+
>
28+
{children}
29+
</div>
30+
);
31+
};
32+
633
interface EditorProps {
734
defaultValue?: string;
835
language?: string;
@@ -21,7 +48,7 @@ interface EditorProps {
2148

2249
const Editor: React.FC<EditorProps> = ({
2350
defaultValue = '',
24-
language = 'javascript',
51+
language = 'plaintext',
2552
theme = 'vs-dark',
2653
height = '100%',
2754
options = {
@@ -259,6 +286,14 @@ const Editor: React.FC<EditorProps> = ({
259286
}
260287
};
261288

289+
// Format document function
290+
const formatDocument = () => {
291+
if (editorRef.current) {
292+
// Trigger Monaco's format document action
293+
editorRef.current.getAction('editor.action.formatDocument')?.run();
294+
}
295+
};
296+
262297
return (
263298
<div className="editor__wrapper">
264299
<MonacoEditor
@@ -273,7 +308,18 @@ const Editor: React.FC<EditorProps> = ({
273308
/>
274309
{showLanguageSelector && (
275310
<div className="editor__toolbar">
276-
<LanguageSelector
311+
<TopTooltip label="Format" children={
312+
<button
313+
className="editor__format-button"
314+
onClick={formatDocument}
315+
aria-label="Format Document"
316+
>
317+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
318+
<path d="M2 4H14M4 8H12M6 12H10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
319+
</svg>
320+
</button>
321+
} />
322+
<SearchableLanguageSelector
277323
value={currentLanguage}
278324
onChange={handleLanguageChange}
279325
className="editor__language-selector"

0 commit comments

Comments
 (0)