Cannot get #CssImport to apply only to component scope - vaadin

I have been wasted hours and days trying to style the Vaadin Upload component. The goal is simple:
sometimes I need the component to show the uploaded file using its built-in file list with allows delete.
other times, I want to hide this list because I have another component such as Grid to show the file details.
Now comes the problem, I can never get it to consistently work using #CssImport with themeFor=vaadin-upload and themeFor=vaadin-upload-file. Vaadin seems to compile the shadow dom and the final result varies, it mixes up the two options and whichever comes last gets applied.
I then thought maybe because the #CssImport is in the #Route component. So, I created two custom upload components that extended the vaadin-upload component with the difference being the different #CssImport (see below). That (frustratingly) still doesn't work. I inspect the document and found that the inside the shadow-dom contains both even though I never use both on the same page.
#CssImport(value = "./css/vaadin-upload-show.css", themeFor = "vaadin-upload")
public class UploadShowFiles extends Upload {
private static final long serialVersionUID = -9198630843136885092L;
public UploadShowFiles(Receiver receiver) {
super(receiver);
}
}
#CssImport(value = "./css/vaadin-upload-hidefile.css", themeFor = "vaadin-upload")
public class UploadHideFiles extends Upload {
private static final long serialVersionUID = 2344860066834705807L;
public UploadHideFiles(Receiver receiver) {
super(receiver);
setClassName("hide-upload");
}
}
The css below will appear in the shadow dom. I expect only display: none or otherwise, not both.
[name="file-list"] {
display: none !important;
height: 0px !important;
}
[name="file-list"] div[part="file-list"] {
display: none !important;
}
[name="file-list"] {
display: block !important;
height: 1.5rem !important;
}
[name="file-list"] div[part="file-list"] {
display: block !important;
}
p/s: This is my first experience using Vaadin in a project and I probably never going to use it again. Customizing anything in Vaadin is so time-consuming and painful.

Styling in the shadow DOM can indeed be tricky. The simplified theming in Vaadin 19 helps a bit.
Where the #CssImport annotation is placed affects if the CSS should be included in the document, but not which components it affects. With themeFor, it will always be applied to all matching components.
What you can do is to use the :host selector to limit which upload components it applies to. Here I am using a class-based approach:
:host(.no-file-list) [name="file-list"] {
display: none !important;
height: 0px !important;
}
:host(.no-file-list) [name="file-list"] div[part="file-list"] {
display: none !important;
}
I can then hide the file list in an upload component by adding a class:
#Route
#CssImport(value = "./styles/upload-style.css", themeFor = "vaadin-upload")
public class FileUploadTest extends VerticalLayout {
public FileUploadTest() {
Upload uploadWithFileList = new Upload();
Upload uploadWithoutFileList = new Upload();
uploadWithoutFileList.addClassName("no-file-list");
add(uploadWithFileList, uploadWithoutFileList);
}
}

Related

Mat-select panel min-width

I'm trying to customize mat-select with multiple checkboxes.
for some reason the panel get wrong min-width as below:
and I don't know where its calculating this min-width.
also I tried to add panelClass and override the min-width from this class,
for example:
<mat-select #multipleSelect (selectionChange)="selectItem($event.value)" panelClass="multiple-panel" multiple>
&.multiple-panel {
min-width: 200px !important;
}
but when opening the dropdown its open with the original width (like in the pic) and after few millisecond"jump" to the custom min-width defined on the panel class.
I find the mat-select very hard to style. anybody knows how to solve this problem?
You can style your mat-select dialog box by giving a panel class (as you mentioned).
Please follow this demo : https://stackblitz.com/edit/angular-matselect-style?file=src/styles.css
to see the styled mat-select components.
Reason :
Reason for the delay is that angular for dialog-boxes, create a cdk-overlay-pane inside the cdk-overlay-container container, So in case of mat-select it provides a min-width of 180px, which is overridden by our panel class in the slight delay.
Yes, there is a slight delay in opening of dialog box and customizing its width to the specified width provided in the panel class. But the delay is acceptable in the project that i was working on. So, you can find the demo for styling the mat-select component, as i have provided 2 components and you can modify any css properties.
Try to use styles using ::ng-deep or :host >>>, if not finding any luck, please paste the styles in style.css.
Update 1 :
Tried css animations, and opacity for making smooth opening of the mat-select options.
.panel-class-applied-on-mat-select {
animation-name: opacityDelay !important;
animation-duration: 0.3s !important;
}
#keyframes opacityDelay {
0% {opacity: 0;}
25% {opacity: 0;}
50% {opacity: 0;}
75% {opacity: 0;}
100% {opacity: 1;}
}
Updated StackBlitz Demo
I used another approach.
Just added this piece of code to global style.
.mat-select-panel {
// some your code
&.ng-animating {
visibility: hidden;
}
}
You can try this solution on
DEMO StackBlitz.
Hack with opacity did not fix jumping width when select is closing.
You'll need to change viewEncapsulation to none at your component decorator.and then add following css to remove the transition effect.Have a look at viewencapsulation in angular docs https://angular.io/guide/component-styles#view-encapsulation.
#Component({
selector: 'app-selector',
templateUrl: './template.html',
styleUrls: ['./template.css'],
encapsulation: ViewEncapsulation.None
})
//CSS
.cdk-overlay-connected-position-bounding-box .cdk-overlay-pane .mat-select-panel.ng-animating {
display: none;
}
Try this way : define a panel class for your mat-select in the code and then in the global/app styling file just add:
.panel-class-name .mat-select-panel {
// add your styling here
}
It worked for me to add some component specific styling for material components.
Please go easy on me S.O. This is my first time contributing. :)
After debugging the console, and running into this issue. Solutions were not clear online. So I'm posting mine here in case someone else runs into this.
I found that there is a width permanently set for the infix class. If you unset it, and optionally add some padding to the right of the value, you'll find that will resolve the issue. Add :host for encapsulation when using ::ng-deep.
Important to Note: ::ng-deep is being permanently deprecated after Angular v14.
There is a property in the #Component() annotation called encapsulation which can be used to turn off the view encapsulation for the component instead of using ::ng-deep.
Solution for the deprecation of ::ng-deep:
#Component({
selector: 'app-selector-name',
template: `<div>Hello World!</div>`,
encapsulation: ViewEncapsulation.None,
styles: [
`
:host mat-form-field .mat-form-field-infix {
width: unset;
}
:host mat-form-field .mat-select-value {
padding-right: 0.5rem; /* 8px */
/* Alternatively, for TailwindCSS: #apply pr-2 */
}
:host .random-class {
/* some encapsulated styling... */
}
.another-random-class {
/* some non-encapsulated styling... */
}
`
]
})
Solution if you do not care about the deprecation of ::ng-deep:
:host ::ng-deep mat-form-field .mat-form-field-infix {
width: unset;
}
:host ::ng-deep mat-form-field .mat-select-value {
padding-right: 0.5rem; /* 8px */
}

Can CssLayout be used to achieve absolute positioning of components?

Currently I am using AbsoluteLayout, but because of some problems I would like to give CssLayout a try.
I would like to dynamically place image-, label- and button-components at specific positions in the layout.
AbsoluteLayout allows me to specify the position like so:
absoluteLayout.addComponent(component, "top:20px;left:20px")
Is something like this at all possible to achieve with CssLayout?
You can apply inline CSS for a component by overriding getCss on CssLayout, so the following should have the wanted result:
CssLayout cssLayout = new CssLayout() {
#Override
protected String getCss(Component component) {
// check the component here and return correct css. In this case only one component in the layout so this works..
return "position: relative; top: 10px; left: 10px";
}
};
cssLayout.setSizeFull();
cssLayout.addComponent(new Button("Hello"));

How to use extra space after tabs in tabsheet

i am new to vaadin , i created tabsheet with two tabs one with graphs and another with some info, my problem was how to add components(combobox, labels) at right corner(same row) of the tabs.
final TabSheet tabSheet = new TabSheet();
tabSheet.setSizeFull();
tabSheet.addTab(rightAndLowerPanels, "Graphs");
tabSheet.addTab(new Label("<b>Haiiiiiiiiiiiii</b>", ContentMode.HTML), "Message");
Ex;
tab1|tab2
I want to add here
i am not able to post image for this problem.
Thanks in advance
While the component itself doesn't support this, it is possible to accomplish this with setting the components absolute position with css so it hovers over the tabsheet in the correct position.
The div(layout) that contains the tabseet and the hovering component should be set position: relative; so that the absolute position is set from the corner of the component not the browser and then set the combobox's position to something like this: position:absolute; right: 3px; top: 3px;.
You can even use Vaadin's AbsoluteLayout or CssLayout if you want to add some checks for determining the components position.
AbsoluteLayout al = new AbsoluteLayout();
al.addStyleName("tab-sheet-layout"); // position: relative;
al.addComponent(new TabSheet(new Label("1"),new Label("2")));
al.addComponent(new ComboBox(), "right: 5px; top: 5px;");
or with CssLayout:
public static class TabSheetLayout extends CssLayout {
public TabSheetLayout() {
addStyleName("tab-sheet-layout"); // position: relative;
addComponent(new TabSheet(new Label("1"),new Label("2")));
addComponent(new ComboBox());
}
#Override
protected String getCss(Component c) {
if (c instanceof ComboBox) { // do some check here
return "position:absolute; right: 3px; top: 3px;";
}
return null;
}
}
You should note however that if you resize the screen small enough, the combobox will be hovering over the tabs, so you need to stop this by fixing the layout width or by some other method.

Transparent background in the WebView in JavaFX

I need to show my webview content over parent background pattern. Is there a straightforward way to do it?
This might be useful
final com.sun.webkit.WebPage webPage = com.sun.javafx.webkit.Accessor.getPageFor(engine);
webPage.setBackgroundColor(0);
Webview transparent I get a good solution here : https://javafx-jira.kenai.com/browse/RT-29186 read comments Harry Hur
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import org.w3c.dom.Document; `
public class TestTranparentApps extends Application {
#Override
public void start(Stage primaryStage) {
new WebPage(primaryStage);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
class WebPage{
WebView webview;
WebEngine webengine;
public WebPage(Stage mainstage){
webview = new WebView();
webengine = webview.getEngine();
Scene scene = new Scene(webview);
scene.setFill(null);
mainstage.setScene(scene);
mainstage.initStyle(StageStyle.TRANSPARENT);
mainstage.setWidth(700);
mainstage.setHeight(100);
webengine.documentProperty().addListener(new DocListener());
webengine.loadContent("<body style='background : rgba(0,0,0,0);font-size: 70px;text-align:center;'>Test Transparent</body>");
}
class DocListener implements ChangeListener<Document>{
#Override
public void changed(ObservableValue<? extends Document> observable, Document oldValue, Document newValue) {
try {
// Use reflection to retrieve the WebEngine's private 'page' field.
Field f = webengine.getClass().getDeclaredField("page");
f.setAccessible(true);
com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(webengine);
page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
} catch (Exception e) {
}
}
}
}
}
If u have a bright background use
webView.setBlendMode(BlendMode.DARKEN);
and when dark background, then
webView.setBlendMode(BlendMode.LIGHTEN);
This will probably be fixed in some time, this feature is requested on the JavaFX bug tracker #RT-25004
You can Chroma Key your WebView over your content using Blend effects.
Update
I tried this out and implementing a true Chroma Key with the built-in Blend effects of JavaFX 2.2 in JavaFX is actually pretty difficult (and surpassed my capabilities of implementing). I managed to make the technique work with pre-chroma keyed flv video formats, but not with arbitrary nodes such as WebView.
Still, for now, you can achieve something somewhat similar in a simple way using the darken and lighten effects as martini suggests in his answer. It's not perfect, but will probably need to suffice until RT-25004 is implemented or the JavaFX platform provides a more comprehensive set of alpha compositing operations.
Since JavaFX 18, transparent WebView backgrounds are finally supported (see JDK-8090547):
WebEngine engine = webView.getEngine();
webView.setPageFill(Color.TRANSPARENT);
You need to set the Web Page Background to 0. But you might need to do some other stuff to not make it break on scrolling. The following GitHub Gist is a java agent that can be added to our app and that manipulates WebPages to be transparent. https://gist.github.com/riccardobl/18603f9de508b1ab6c9e or more up to date version https://github.com/FAForever/downlords-faf-client/blob/develop/webview-patch/src/main/java/com/faforever/client/webviewpatcher/TransparentWebViewPatch.java
Only solution that I found and was fully working.
I'm resurrecting this because I found a better solution for the problem of having a WebView over a gradient. Suppose you have a gradient like this and want to display an HTML string over it in white color that looks something like this:
<p>Integer semper, est imperdiet mattis porttitor, massa vulputate ipsum</p>
The trick is to convert your gradient to Base64 string:
iVBORw0KGgoAAAANSUhEUgAAAAEAAAHDCAYAAADlQnCCAAAAq0lEQVRIib2VwRWAMAhDaSdwCtd1No8uUzzp0ydBgjwvnmhCPrTKtKxbb73NXcaQLiL5j/olyhQH5JXu4H4sUGyWHAIa9VWjU9qtyAh6JGicIMJno2HcGSX3gJsCKH5EfUfHJUed+qFzekrpuVzM/oq4uFKGr/pI4B7wiP2Vgno0F/uCUQ9ZXWg4vM/JL1Hpt10Nt+hZjhCDKepxV8H3soZGWOqHP3ZSGYDdATTkg3iGU3JnAAAAAElFTkSuQmCC
Since WebView loads any HTML string, this can involve <body> that has a background. In my instance, the WebView was not occupying the whole space with the gradient, so I had to position the background as well. Following is the HTML I added to the HTML string above at the beggining:
<html>
<head>
</head>
<body style="margin: 0px; padding: 0px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAHDCAYAAADlQnCCAAAAq0lEQVRIib2VwRWAMAhDaSdwCtd1No8uUzzp0ydBgjwvnmhCPrTKtKxbb73NXcaQLiL5j/olyhQH5JXu4H4sUGyWHAIa9VWjU9qtyAh6JGicIMJno2HcGSX3gJsCKH5EfUfHJUed+qFzekrpuVzM/oq4uFKGr/pI4B7wiP2Vgno0F/uCUQ9ZXWg4vM/JL1Hpt10Nt+hZjhCDKepxV8H3soZGWOqHP3ZSGYDdATTkg3iGU3JnAAAAAElFTkSuQmCC); background-repeat: repeat-x; background-position: 0 -152px;">
<span style="font-family: Arial; font-size: 12px; color: white; display: block; margin:0px;">
And at the end:
</span></body></html>
Therefore producing such code:
String desc = "<html><head></head><body style=\"margin: 0px; padding: 0px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAHDCAYAAADlQnCCAAAAq0lEQVRIib2VwRWAMAhDaSdwCtd1No8uUzzp0ydBgjwvnmhCPrTKtKxbb73NXcaQLiL5j/olyhQH5JXu4H4sUGyWHAIa9VWjU9qtyAh6JGicIMJno2HcGSX3gJsCKH5EfUfHJUed+qFzekrpuVzM/oq4uFKGr/pI4B7wiP2Vgno0F/uCUQ9ZXWg4vM/JL1Hpt10Nt+hZjhCDKepxV8H3soZGWOqHP3ZSGYDdATTkg3iGU3JnAAAAAElFTkSuQmCC); background-repeat: repeat-x; background-position: 0 -152px;\"><span style=\"font-family: Arial; font-size: 12px; color: white; display: block; margin:0px;\">" + description + "</span></body></html>";
wv_desc.getEngine().loadContent(desc);
You could probably put the styles in the <head> or load them some other way for clarity I suppose, but this is a much better work-around to anything else I found on the net for this issue.

How to remove rounded corners from one jQuery UI widget but not the others?

My particular problem is that I want the autocomplete function to not have round corners, but all the other widgets that have round corners should.
Is there a parameter I can pass to disable the corners just for the autocomplete?
Edit
Let's see if this can be answered.
On page Datepicker.
I'd like to remove all round-corner classes from appearing (the header and the next-previous buttons).
$( "#datepicker" ).datepicker('widget').removeClass('ui-corner-all'); would not work.
Very late but here it goes:
jQuery UI widgets have a method, which returns the HTML node for the widget itself.
So the answer would be:
$('#someinput').autocomplete(...).autocomplete('widget').removeClass('ui-corner-all');
Responding to the EDIT:
As far I can see, you need to chain widget() method with autocomplete() (or datepicker()) method for it to work. Seems like it doesn't work for regular HTML nodes returned by $().
assign this css class to the element with corners of your widget.
.ui-corner-flat {
border-top-left-radius: 0px !important;
border-top-right-radius: 0px !important;
border-bottom-left-radius: 0px !important;
border-bottom-right-radius: 0px !important;
}
$("#elementwithcorners").addClass("ui-corner-flat");
to remove the bottom left radius
in the constructor I did this
$( "#signup" ).dialog(
{
create: function (event, ui) {
$(".ui-dialog").css('border-bottom-left-radius','0px');
},
}
);
The _suggest() method of the Autocomplete widget calls menu.refresh(), and therefore resets the ui-corner-all class for menu items, etc., each time the input changes. However, the open() callback is called after every menu.refresh() call within _suggest(), and so is a sensible place to adjust classes as desired:
$("#autocomplete").autocomplete("option", {
open: function(event, ui) {
$(this).autocomplete("widget")
.menu("widget").removeClass("ui-corner-all")
.find(".ui-corner-all").removeClass("ui-corner-all");
}
});
The Datepicker widget is a little tougher, as it's built to be sort of a semi-singleton. Here we need a monkey patch to do it consistently, since none of the supplied callback options is suitable:
// store the built-in update method on the "global" instance...
$.datepicker.__updateDatepicker = $.datepicker._updateDatepicker;
// ...and then clobber with our fix
$.datepicker._updateDatepicker = function(inst) {
$.datepicker.__updateDatepicker(inst);
inst.dpDiv.removeClass("ui-corner-all")
.find(".ui-corner-all").removeClass("ui-corner-all");
};
Note that the default _updateDatepicker() implementation has no return value. Also, note that the _updateDatepicker() method is not an interface method, so should not be assumed to be available. As such, the most consistent way to accomplish the corner fix is with appropriate CSS, along the lines of:
.ui-autocomplete.ui-menu.ui-corner-all,
.ui-autocomplete.ui-menu .ui-menu-item > a.ui-corner-all,
.ui-datepicker.ui-corner-all,
.ui-datepicker-header.ui-corner-all,
.ui-datepicker-next.ui-corner-all,
.ui-datepicker-prev.ui-corner-all {
border-radius: 0;
}
More specificity (or the !important directive) may be used to ensure these selectors are respected. This is exactly why jQuery uses theme classes – fudging these things in is an interesting hack, but it's the less clean option unless style is unavailable…
Create a new CSS class for the element you don't want rounded corners.
p.rounded { border-radius: 10px; }
p.none-rounded { border-radius: 0; }

Resources