Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,55 @@
</dependencies>
</profile>

<profile>
<id>dance</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<configuration>
Comment on lines +325 to +327

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether maven-clean-plugin version is managed in a parent POM
fd pom.xml | xargs rg -n 'maven-clean-plugin' -A3

Repository: FlowingCode/CommonsDemo

Length of output: 402


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- base/pom.xml excerpt ---'
sed -n '300,350p' base/pom.xml

echo
echo '--- repository pom files ---'
fd -a pom.xml

echo
echo '--- maven-clean-plugin occurrences ---'
rg -n 'maven-clean-plugin|<pluginManagement>|<parent>|<version>' -g 'pom.xml' -A4 -B4 .

Repository: FlowingCode/CommonsDemo

Length of output: 17541


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,45p' base/pom.xml

Repository: FlowingCode/CommonsDemo

Length of output: 1709


Pin maven-clean-plugin or manage it centrally base/pom.xml:324-327 — this plugin has no local <version>, and it isn’t defined in the repo’s pluginManagement, so the build depends on Maven’s default resolution. Pin a version here or add it to shared pluginManagement for reproducible builds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@base/pom.xml` around lines 325 - 327, The maven-clean-plugin declaration in
the base pom is missing an explicit version and is not covered by shared
pluginManagement, so the build can resolve it unpredictably. Update the plugin
entry in the pom where maven-clean-plugin is declared to include a pinned
version, or add that version centrally under the repository’s pluginManagement
so all modules use the same reproducible plugin version.

<filesets>
<fileset>
<directory>${project.basedir}</directory>
<includes>
<include>package.json</include>
<include>package-lock.json</include>
<include>tsconfig.json</include>
<include>tsconfig.json.*</include>
<include>types.d.ts</include>
<include>types.d.ts.*</include>
<include>vite.config.ts</include>
<include>vite.generated.ts</include>
<include>webpack.config.js</include>
<include>webpack.generated.js</include>
</includes>
</fileset>
<fileset>
<directory>${project.basedir}/frontend</directory>
<includes>
<include>index.html</include>
</includes>
</fileset>
<fileset>
<directory>${project.basedir}/frontend/generated</directory>
</fileset>
<fileset>
<directory>${project.basedir}/node_modules</directory>
</fileset>
<fileset>
<directory>${project.basedir}/src/main/bundles</directory>
</fileset>
<fileset>
<directory>${project.basedir}/src/main/dev-bundle</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</build>
</profile>

</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*-
* #%L
* Commons Demo
* %%
* Copyright (C) 2020 - 2025 Flowing Code
* %%
* 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.
* #L%
*/
package com.flowingcode.vaadin.addons.demo;

import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.icon.IconFactory;
import java.util.Locale;

/**
* CommonsDemo icons.
*
* @author Javier Godoy / Flowing Code
*/
public enum CommonsDemoIcons implements IconFactory {
ROTATE, FLIP, HIDE_SOURCE, SHOW_SOURCE;

/**
* The Iconset name, i.e. {@code "fab"}."
*/
public static final String ICONSET = "commons-demo";

/**
* Return the full icon name.
*
* @return the full icon name, i.e. {@code "commons-demo:name"}..
*/
public String getIconName() {
return ICONSET + ':' + getIconPart();
}

/**
* Return the icon name within the iconset.
*
* @return the icon name, i.e. {@code "name"}..
*/
public String getIconPart() {
return name().toLowerCase(Locale.ENGLISH).replace('_', '-').replaceFirst("^-", "");
}

/**
* Create a new {@link Icon} instance with the icon determined by the name.
*
* @return a new instance of {@link Icon} component
*/
@Override
public Icon create() {
return new Icon(getIconPart());
}

/**
* Server side component for {@code Brands}
*/
@JsModule("./commons-demo-iconset.ts")
@SuppressWarnings("serial")
public static final class Icon extends com.vaadin.flow.component.icon.Icon {
private Icon(String icon) {
super(ICONSET, icon);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import com.vaadin.flow.component.HasElement;
import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.dom.Element;
Expand All @@ -51,13 +53,130 @@
}

public SourceCodeViewer(String url, String language, Map<String, String> properties) {
addClassName("source-code-viewer");
addClassName("has-code-viewer-gutter");

codeViewer = new Element("code-viewer");
getElement().appendChild(codeViewer);
getElement().getStyle().set("overflow", "auto");
getElement().getStyle().set("display", "flex");
codeViewer.getStyle().set("flex-grow", "1");

Div codeViewerWrapper = new Div();
codeViewerWrapper.addClassName("source-code-viewer-codeviewer-wrapper");
codeViewerWrapper.getElement().appendChild(codeViewer);

Button showButton = new Button(CommonsDemoIcons.SHOW_SOURCE.create());
showButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY_INLINE);
showButton.setAriaLabel("Show source code");
showButton.addClickListener(ev -> setSourceCollapsed(false));
showButton.addClassName("source-code-viewer-button");

Check failure on line 69 in base/src/main/java/com/flowingcode/vaadin/addons/demo/SourceCodeViewer.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "source-code-viewer-button" 4 times.

See more on https://sonarcloud.io/project/issues?id=FlowingCode_CommonsDemo&issues=AZ8bFgN_Yi8n4DQP_l9b&open=AZ8bFgN_Yi8n4DQP_l9b&pullRequest=160
showButton.addClassName("source-code-viewer-show-button");

Button hideButton = new Button(CommonsDemoIcons.HIDE_SOURCE.create());
hideButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY_INLINE);
hideButton.setAriaLabel("Hide source code");
hideButton.addClickListener(ev -> setSourceCollapsed(true));
hideButton.addClassName("source-code-viewer-button");
hideButton.addClassName("source-code-viewer-hide-button");

Button rotateButton = new Button(CommonsDemoIcons.ROTATE.create());
rotateButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY_INLINE);
rotateButton.setAriaLabel("Rotate source code");
rotateButton.addClickListener(ev -> rotateSource());
rotateButton.addClassName("source-code-viewer-button");
rotateButton.addClassName("source-code-viewer-rotate-button");

Button flipButton = new Button(CommonsDemoIcons.FLIP.create());
flipButton.addThemeVariants(ButtonVariant.LUMO_ICON, ButtonVariant.LUMO_TERTIARY_INLINE);
flipButton.setAriaLabel("Flip source code");
flipButton.addClickListener(ev -> flipSource());
flipButton.addClassName("source-code-viewer-button");
flipButton.addClassName("source-code-viewer-flip-button");

Div buttons = new Div(showButton, hideButton, flipButton, rotateButton);
buttons.addClassName("source-code-viewer-buttons");

// Non-scrolling overlay so the buttons stay pinned while the code scrolls
Div buttonsWrapper = new Div(buttons);
buttonsWrapper.addClassName("source-code-viewer-buttons-wrapper");

add(codeViewerWrapper, buttonsWrapper);

setProperties(properties);
addAttachListener(ev -> fetchContents(url, language));
addAttachListener(ev -> {
fetchContents(url, language);
observeScrollbar();
});
}

/**
* Observes the scrollable wrapper. Whenever the vertical scrollbar appears or disappears, sets (or
* clears) the {@code --code-viewer-gutter} custom property on the nearest ancestor (or self)
* carrying the {@code has-code-viewer-gutter} class. Whenever the wrapper collapses below 24px in
* either axis, sets {@code --source-code-viewer-show-button-display} so the show button becomes
* visible (and clears it otherwise).
*/
private void observeScrollbar() {
getElement().executeJs(
"""
const root = this;
const wrapper = root.querySelector('.source-code-viewer-codeviewer-wrapper');
if (!wrapper) return;
root.__scrollbarObserver?.disconnect();
root.__scrollbarMutation?.disconnect();
let hasScrollbar = null;
const update = () => {
if (wrapper.offsetWidth < 24 || wrapper.offsetHeight < 10) {
root.style.setProperty('--source-code-viewer-show-button-display', 'block');
} else {
root.style.removeProperty('--source-code-viewer-show-button-display');
}
const current = wrapper.scrollHeight > wrapper.clientHeight;
if (current === hasScrollbar) return;
hasScrollbar = current;
let target = root;
while (target && !target.classList.contains('has-code-viewer-gutter')) {
target = target.parentElement;
}
if (target) {
if (current) {
const scrollbarWidth = wrapper.offsetWidth - wrapper.clientWidth;
target.style.setProperty('--code-viewer-gutter', scrollbarWidth + 'px');
} else {
target.style.removeProperty('--code-viewer-gutter');
}
}
};
let frame = 0;
const scheduleUpdate = () => {
if (frame) return;
frame = requestAnimationFrame(() => { frame = 0; update(); });
};
const resizeObserver = new ResizeObserver(scheduleUpdate);
resizeObserver.observe(wrapper);
root.__scrollbarObserver = resizeObserver;
const codeViewer = root.querySelector('code-viewer');
if (codeViewer) {
const mutationObserver = new MutationObserver(scheduleUpdate);
mutationObserver.observe(codeViewer, {childList: true, subtree: true});
root.__scrollbarMutation = mutationObserver;
}
update();
""");
}

private void setSourceCollapsed(boolean collapsed) {
getElement().executeJs(
"this.dispatchEvent(new CustomEvent('source-collapse-changed',"
+ " {bubbles: true, detail: {collapsed: $0}}))",
collapsed);
}

private void rotateSource() {
getElement().executeJs(
"this.dispatchEvent(new CustomEvent('source-rotate', {bubbles: true}))");
}

private void flipSource() {
getElement().executeJs(
"this.dispatchEvent(new CustomEvent('source-flip', {bubbles: true}))");
}

public void fetchContents(String url, String language) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ public boolean isEmpty() {
return code.isEmpty();
}

private void setSourcePosition(SourcePosition position) {
public SourcePosition getSourcePosition() {
return sourcePosition;
}

public void setSourcePosition(SourcePosition position) {
if (!position.equals(sourcePosition)) {
getContent().removeAll();
switch (position) {
Expand All @@ -76,10 +80,6 @@ private void setSourcePosition(SourcePosition position) {
}
}

public void toggleSourcePosition() {
setSourcePosition(sourcePosition.toggle());
}

public void setOrientation(Orientation o) {
getContent().setOrientation(o);
getContent()
Expand Down
Loading
Loading