Skip to content

Commit f56d1c6

Browse files
javier-godoypaodb
authored andcommitted
feat: implement code block highlight
1 parent be9c9f9 commit f56d1c6

File tree

5 files changed

+188
-0
lines changed

5 files changed

+188
-0
lines changed

src/main/java/com/flowingcode/vaadin/addons/demo/SourceCodeViewer.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
package com.flowingcode.vaadin.addons.demo;
2121

2222
import com.flowingcode.vaadin.addons.DevSourceRequestHandler;
23+
import com.vaadin.flow.component.Component;
2324
import com.vaadin.flow.component.HasSize;
25+
import com.vaadin.flow.component.UI;
2426
import com.vaadin.flow.component.dependency.JsModule;
2527
import com.vaadin.flow.component.dependency.NpmPackage;
2628
import com.vaadin.flow.component.html.Div;
@@ -80,4 +82,15 @@ private void setProperties(Map<String, String> properties) {
8082
codeViewer.setPropertyJson("env", env);
8183
}
8284
}
85+
86+
public static void highlightOnHover(Component c, String id) {
87+
c.addAttachListener(ev -> {
88+
c.getElement().executeJs("Vaadin.Flow.fcCodeViewerConnector.highlightOnHover(this,$0)", id);
89+
});
90+
}
91+
92+
public static void highlight(String id) {
93+
UI.getCurrent().getElement().executeJs("Vaadin.Flow.fcCodeViewerConnector.highlight($0)", id);
94+
}
95+
8396
}

src/main/resources/META-INF/resources/frontend/code-viewer.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,27 @@ import {
2525

2626
import "./prism.js";
2727

28+
(function () {
29+
(window as any).Vaadin.Flow.fcCodeViewerConnector = {
30+
31+
//highlight a marked block in the single code-viewer of the UI
32+
highlight: (id:string|null) => {
33+
const viewer = document.querySelector("code-viewer") as CodeViewer;
34+
if (viewer) viewer.highligth(id);
35+
},
36+
37+
//highlight a marked block in the single code-viewer of the UI, on hover of element
38+
highlightOnHover: (element : HTMLElement, id:string) => {
39+
element.addEventListener('mouseenter', ()=>{
40+
(window as any).Vaadin.Flow.fcCodeViewerConnector.highlight(id);
41+
});
42+
element.addEventListener('mouseleave', ()=>{
43+
(window as any).Vaadin.Flow.fcCodeViewerConnector.highlight(null);
44+
});
45+
}
46+
}
47+
})();
48+
2849
@customElement("code-viewer")
2950
export class CodeViewer extends LitElement {
3051

@@ -177,13 +198,22 @@ pre[class*="language-"] {
177198
display: block;
178199
background-color: #272822;
179200
font-size: 10pt;
201+
position: relative;
180202
}
181203
182204
pre[class*="language-"] {
183205
background: inherit;
184206
}
207+
208+
.highlight {
209+
position: absolute;
210+
background: rgba(255,255,128,25%);
211+
right: 0;
212+
left: 0;
213+
}
185214
</style>
186215
216+
<div class='highlight'></div>
187217
<pre><code id="code"></code></pre>
188218
`;
189219
}
@@ -203,6 +233,7 @@ pre[class*="language-"] {
203233

204234
(window as any).Prism.highlightAllUnder(self);
205235
self.__license.reverse().forEach(e=>self.querySelector('pre code')?.prepend(e));
236+
self.process(code);
206237
}};
207238
xhr.open('GET', sourceUrl, true);
208239
xhr.send();
@@ -302,6 +333,8 @@ pre[class*="language-"] {
302333
&& !line.startsWith('@SuppressWarnings')
303334
&& !line.startsWith('@Ignore')
304335
&& !line.startsWith('package ')
336+
&& !line.trim().startsWith('SourceCodeViewer.highlightOnHover(')
337+
&& !line.trim().startsWith('SourceCodeViewer.highlight(')
305338
&& line != 'import com.vaadin.flow.router.PageTitle;'
306339
&& line != 'import com.vaadin.flow.router.Route;'
307340
&& line != 'import com.flowingcode.vaadin.addons.demo.DemoSource;'
@@ -361,4 +394,68 @@ pre[class*="language-"] {
361394
return 0;
362395
}
363396

397+
398+
//process begin-block and end-block instructions
399+
process(code : HTMLElement) {
400+
var nodes = code.childNodes;
401+
402+
var trimEnd = (i:number) => {
403+
//remove trailing \n and spaces from text node i
404+
const node = nodes[i]
405+
if (node && node.nodeType==3) {
406+
node.textContent=(node.textContent as any).replaceAll(/\n[\t\x20]+$/g,'');
407+
}
408+
}
409+
410+
var last : string|undefined;
411+
for (var i=0; i<nodes.length; i++) {
412+
//process instructions in element nodes
413+
if (nodes[i].nodeType!=1) continue;
414+
415+
const text = nodes[i].textContent!;
416+
var m = text.match("^//\\s*begin-block\\s+(\\S+)\\s*");
417+
418+
if (m) {
419+
last = m[1];
420+
(nodes[i] as HTMLElement).classList.add('begin-'+m[1]);
421+
nodes[i].textContent='';
422+
trimEnd(i-1);
423+
continue;
424+
}
425+
426+
if (text.match("^//\\s*end-block\\s*") && last) {
427+
(nodes[i] as HTMLElement).classList.add('end-'+last);
428+
nodes[i].textContent='';
429+
trimEnd(i-1);
430+
continue;
431+
}
432+
}
433+
434+
}
435+
436+
//highligth a marked block
437+
highligth(id:string|null) {
438+
const div = this.querySelector('.highlight') as HTMLElement;
439+
440+
div.style.removeProperty('top');
441+
div.style.removeProperty('height');
442+
if (id!==null) {
443+
var begin = this.querySelector('.begin-'+id) as HTMLElement;
444+
var end = this.querySelector('.end-'+id) as HTMLElement;
445+
if (begin && end && begin.offsetTop<=end.offsetTop) {
446+
var top = begin.offsetTop;
447+
var height = end.offsetTop+end.offsetHeight-top;
448+
div.style.top= `calc( ${top}px + 0.75em)`;
449+
div.style.height= `${height}px`;
450+
451+
//scroll to the begin of the marked block
452+
if ((begin as any).scrollIntoViewIfNeeded) {
453+
(begin as any).scrollIntoViewIfNeeded();
454+
} else {
455+
(begin as any).scrollIntoView()
456+
}
457+
}
458+
}
459+
}
364460
}
461+

src/test/java/com/flowingcode/vaadin/addons/demo/Demo.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public Demo() {
3232
addDemo(new LegacyDemo());
3333
addDemo(SampleDemo.class, "Demo");
3434
addDemo(SampleDemoDefault.class);
35+
addDemo(SampleDemoHighlight.class);
3536
addDemo(AdHocDemo.class);
3637
}
3738
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*-
2+
* #%L
3+
* Commons Demo
4+
* %%
5+
* Copyright (C) 2020 - 2023 Flowing Code
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.flowingcode.vaadin.addons.demo;
21+
22+
import com.vaadin.flow.component.Text;
23+
import com.vaadin.flow.component.button.Button;
24+
import com.vaadin.flow.component.dependency.StyleSheet;
25+
import com.vaadin.flow.component.html.Div;
26+
import com.vaadin.flow.component.html.Span;
27+
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
28+
import com.vaadin.flow.router.PageTitle;
29+
import com.vaadin.flow.router.Route;
30+
31+
@Route(value = "demo/highlight", layout = Demo.class)
32+
@PageTitle("Highlight")
33+
@DemoSource
34+
@StyleSheet("./highlight-demo.css")
35+
public class SampleDemoHighlight extends Div {
36+
37+
public SampleDemoHighlight() {
38+
add(new Span("Highlight source fragments"));
39+
40+
// begin-block first
41+
Div first = new Div(new Text("First"));
42+
SourceCodeViewer.highlightOnHover(first, "first");
43+
first.addClassName("dashed"); // hide-source
44+
add(first);
45+
// end-block
46+
47+
// begin-block second
48+
Div second = new Div(new Text("Second"));
49+
SourceCodeViewer.highlightOnHover(second, "second");
50+
second.addClassName("dashed"); // hide-source
51+
add(second);
52+
// end-block
53+
54+
HorizontalLayout hl = new HorizontalLayout();
55+
56+
// begin-block button
57+
hl.add(new Button("Click me", ev -> {
58+
SourceCodeViewer.highlight("button");
59+
}));
60+
// end-block
61+
62+
hl.add(new Button("Highlight Off", ev -> {
63+
SourceCodeViewer.highlight(null);
64+
}));
65+
66+
add(hl);
67+
}
68+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.dashed {
2+
border: 1px dashed black;
3+
padding: 1ex;
4+
margin: 1ex;
5+
}
6+
7+
.dashed:hover {
8+
background: var(--lumo-contrast-10pct);
9+
}

0 commit comments

Comments
 (0)