Add Notification control
This commit is contained in:
parent
24a2e096ad
commit
c1f9a76e1e
@ -72,10 +72,7 @@ public class Message extends TileBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The property representing the user specified close handler. Note that
|
* The property representing the user specified close handler.
|
||||||
* if you have also specified the ModalPane instance or CSS selector, this
|
|
||||||
* handler will be executed after the default close handler. Therefore, you
|
|
||||||
* can use it to perform arbitrary actions on dialog close.
|
|
||||||
*/
|
*/
|
||||||
protected final ObjectProperty<EventHandler<? super Event>> onClose =
|
protected final ObjectProperty<EventHandler<? super Event>> onClose =
|
||||||
new SimpleObjectProperty<>(this, "onClose");
|
new SimpleObjectProperty<>(this, "onClose");
|
||||||
|
@ -27,7 +27,7 @@ public class MessageSkin extends TileSkinBase<Message> {
|
|||||||
o -> pseudoClassStateChanged(Styles.STATE_INTERACTIVE, getSkinnable().getActionHandler() != null)
|
o -> pseudoClassStateChanged(Styles.STATE_INTERACTIVE, getSkinnable().getActionHandler() != null)
|
||||||
);
|
);
|
||||||
|
|
||||||
root.setOnMouseClicked(e -> {
|
container.setOnMouseClicked(e -> {
|
||||||
if (getSkinnable().getActionHandler() != null) {
|
if (getSkinnable().getActionHandler() != null) {
|
||||||
getSkinnable().getActionHandler().run();
|
getSkinnable().getActionHandler().run();
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ public class MessageSkin extends TileSkinBase<Message> {
|
|||||||
layoutInArea(closeButton, w - lb.getWidth() - 5, 5, lb.getWidth(), lb.getHeight(), -1, HPos.RIGHT,
|
layoutInArea(closeButton, w - lb.getWidth() - 5, 5, lb.getWidth(), lb.getHeight(), -1, HPos.RIGHT,
|
||||||
VPos.TOP);
|
VPos.TOP);
|
||||||
}
|
}
|
||||||
layoutInArea(root, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
|
layoutInArea(container, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
168
base/src/main/java/atlantafx/base/controls/Notification.java
Normal file
168
base/src/main/java/atlantafx/base/controls/Notification.java
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.base.controls;
|
||||||
|
|
||||||
|
import javafx.beans.NamedArg;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.event.Event;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Control;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.control.Skin;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Notification control is intended for displaying alerts and messages
|
||||||
|
* to users as pop-ups. It's customizable with different colors and icons,
|
||||||
|
* can contain a graphic or image, along with the message and additional actions
|
||||||
|
* for users to take. The purpose of this control is to immediately notify users
|
||||||
|
* of significant events and provide them with quick access to related actions without
|
||||||
|
* interrupting their workflow.
|
||||||
|
*/
|
||||||
|
public class Notification extends Control {
|
||||||
|
|
||||||
|
public Notification() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Notification(@Nullable @NamedArg("message") String message) {
|
||||||
|
this(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Notification(@Nullable @NamedArg("message") String message,
|
||||||
|
@Nullable @NamedArg("graphic") Node graphic) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
setMessage(message);
|
||||||
|
setGraphic(graphic);
|
||||||
|
|
||||||
|
// set reasonable default width
|
||||||
|
setPrefWidth(400);
|
||||||
|
setMaxWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
|
getStyleClass().add("notification");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Skin<?> createDefaultSkin() {
|
||||||
|
return new NotificationSkin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Properties //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an optional graphical component that can be displayed alongside
|
||||||
|
* the notification message.
|
||||||
|
*/
|
||||||
|
private final ObjectProperty<Node> graphic = new SimpleObjectProperty<>(this, "graphic");
|
||||||
|
|
||||||
|
public Node getGraphic() {
|
||||||
|
return graphic.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<Node> graphicProperty() {
|
||||||
|
return graphic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGraphic(Node graphic) {
|
||||||
|
this.graphic.set(graphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a short text message that will be displayed to users when the
|
||||||
|
* notification appears. This property doesn't support the formatted text.
|
||||||
|
*/
|
||||||
|
private final StringProperty message = new SimpleStringProperty(this, "message");
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty messageProperty() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message.set(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the primary actions associated with this notification.
|
||||||
|
*
|
||||||
|
* <p>This property is used to store one or more action buttons that will
|
||||||
|
* be displayed at the bottom of the notification when it appears.
|
||||||
|
*/
|
||||||
|
private final ReadOnlyObjectWrapper<ObservableList<Button>> primaryActions =
|
||||||
|
new ReadOnlyObjectWrapper<>(this, "primaryActions", FXCollections.observableArrayList());
|
||||||
|
|
||||||
|
public ObservableList<Button> getPrimaryActions() {
|
||||||
|
return primaryActions.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<ObservableList<Button>> primaryActionsProperty() {
|
||||||
|
return primaryActions.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryActions(ObservableList<Button> buttons) {
|
||||||
|
this.primaryActions.set(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrimaryActions(Button... buttons) {
|
||||||
|
getPrimaryActions().setAll(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the secondary actions associated with this notification.
|
||||||
|
*
|
||||||
|
* <p>This property is used to store one or more menu items that will be displayed
|
||||||
|
* as a dropdown menu at the top corner of the notification when it appears.
|
||||||
|
*/
|
||||||
|
private final ReadOnlyObjectWrapper<ObservableList<MenuItem>> secondaryActions =
|
||||||
|
new ReadOnlyObjectWrapper<>(this, "secondaryActions", FXCollections.observableArrayList());
|
||||||
|
|
||||||
|
public ObservableList<MenuItem> getSecondaryActions() {
|
||||||
|
return secondaryActions.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<ObservableList<MenuItem>> secondaryActionsProperty() {
|
||||||
|
return secondaryActions.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecondaryActions(ObservableList<MenuItem> items) {
|
||||||
|
this.secondaryActions.set(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecondaryActions(MenuItem... items) {
|
||||||
|
getSecondaryActions().setAll(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the close handler used to dismiss this notification.
|
||||||
|
*/
|
||||||
|
protected final ObjectProperty<EventHandler<? super Event>> onClose =
|
||||||
|
new SimpleObjectProperty<>(this, "onClose");
|
||||||
|
|
||||||
|
public EventHandler<? super Event> getOnClose() {
|
||||||
|
return onClose.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<EventHandler<? super Event>> onCloseProperty() {
|
||||||
|
return onClose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnClose(EventHandler<? super Event> onClose) {
|
||||||
|
this.onClose.set(onClose);
|
||||||
|
}
|
||||||
|
}
|
176
base/src/main/java/atlantafx/base/controls/NotificationSkin.java
Normal file
176
base/src/main/java/atlantafx/base/controls/NotificationSkin.java
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.base.controls;
|
||||||
|
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.event.Event;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.ButtonBar;
|
||||||
|
import javafx.scene.control.ContextMenu;
|
||||||
|
import javafx.scene.control.SkinBase;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
|
||||||
|
public class NotificationSkin extends SkinBase<Notification> {
|
||||||
|
|
||||||
|
protected final VBox container = new VBox();
|
||||||
|
protected final HBox header = new HBox();
|
||||||
|
|
||||||
|
protected final StackPane graphicSlot = new StackPane();
|
||||||
|
protected final ChangeListener<Node> graphicSlotListener = new SlotListener(graphicSlot);
|
||||||
|
protected final TextFlow messageText = new TextFlow();
|
||||||
|
|
||||||
|
protected final StackPane closeButton = new StackPane();
|
||||||
|
protected final StackPane closeButtonIcon = new StackPane();
|
||||||
|
protected final StackPane menuButton = new StackPane();
|
||||||
|
protected final StackPane menuButtonIcon = new StackPane();
|
||||||
|
protected final ContextMenu actionsMenu = new ContextMenu();
|
||||||
|
protected final HBox actionsBox = new HBox();
|
||||||
|
|
||||||
|
protected final ButtonBar buttonBar = new ButtonBar();
|
||||||
|
|
||||||
|
protected NotificationSkin(Notification control) {
|
||||||
|
super(control);
|
||||||
|
|
||||||
|
// == GRAPHIC ==
|
||||||
|
|
||||||
|
graphicSlot.getStyleClass().add("graphic");
|
||||||
|
control.graphicProperty().addListener(graphicSlotListener);
|
||||||
|
graphicSlotListener.changed(control.graphicProperty(), null, control.getGraphic());
|
||||||
|
|
||||||
|
// == MESSAGE ==
|
||||||
|
|
||||||
|
messageText.getStyleClass().add("message");
|
||||||
|
HBox.setHgrow(messageText, Priority.ALWAYS);
|
||||||
|
|
||||||
|
setMessageText();
|
||||||
|
registerChangeListener(control.messageProperty(), o -> setMessageText());
|
||||||
|
|
||||||
|
// text wrapping won't work without this
|
||||||
|
messageText.setMaxWidth(Region.USE_PREF_SIZE);
|
||||||
|
messageText.setMinHeight(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
|
// == TOP BUTTONS ==
|
||||||
|
|
||||||
|
menuButton.getStyleClass().add("secondary-menu-button");
|
||||||
|
menuButton.getChildren().setAll(menuButtonIcon);
|
||||||
|
menuButton.setOnMouseClicked(e -> actionsMenu.show(
|
||||||
|
menuButton,
|
||||||
|
menuButton.localToScreen(menuButton.getLayoutBounds()).getMinX(),
|
||||||
|
menuButton.localToScreen(menuButton.getLayoutBounds()).getMaxY()
|
||||||
|
));
|
||||||
|
menuButton.setVisible(!getSkinnable().getSecondaryActions().isEmpty());
|
||||||
|
menuButton.setManaged(!getSkinnable().getSecondaryActions().isEmpty());
|
||||||
|
menuButtonIcon.getStyleClass().add("icon");
|
||||||
|
|
||||||
|
Bindings.bindContent(actionsMenu.getItems(), getSkinnable().getSecondaryActions());
|
||||||
|
registerListChangeListener(actionsMenu.getItems(), o -> {
|
||||||
|
menuButton.setVisible(!getSkinnable().getSecondaryActions().isEmpty());
|
||||||
|
menuButton.setManaged(!getSkinnable().getSecondaryActions().isEmpty());
|
||||||
|
});
|
||||||
|
|
||||||
|
closeButton.getStyleClass().add("close-button");
|
||||||
|
closeButton.getChildren().setAll(closeButtonIcon);
|
||||||
|
closeButton.setOnMouseClicked(e -> handleClose());
|
||||||
|
closeButton.setVisible(control.getOnClose() != null);
|
||||||
|
closeButton.setManaged(control.getOnClose() != null);
|
||||||
|
closeButtonIcon.getStyleClass().add("icon");
|
||||||
|
|
||||||
|
registerChangeListener(control.onCloseProperty(), o -> {
|
||||||
|
closeButton.setVisible(getSkinnable().getOnClose() != null);
|
||||||
|
closeButton.setManaged(getSkinnable().getOnClose() != null);
|
||||||
|
});
|
||||||
|
|
||||||
|
actionsBox.getStyleClass().add("actions");
|
||||||
|
actionsBox.getChildren().setAll(menuButton, closeButton);
|
||||||
|
actionsBox.setFillHeight(false);
|
||||||
|
HBox.setMargin(actionsBox, new Insets(-8, -8, 0, 0));
|
||||||
|
|
||||||
|
// == HEADER ==
|
||||||
|
|
||||||
|
// use pref size for slots, or they will be resized
|
||||||
|
// to the bare minimum due to Priority.ALWAYS
|
||||||
|
graphicSlot.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
actionsBox.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
|
// do not resize children or container won't restore
|
||||||
|
// to its original size after expanding
|
||||||
|
header.setFillHeight(false);
|
||||||
|
header.getStyleClass().add("header");
|
||||||
|
header.getChildren().setAll(graphicSlot, messageText, actionsBox);
|
||||||
|
header.setAlignment(Pos.TOP_LEFT);
|
||||||
|
|
||||||
|
// == BUTTON BAR ==
|
||||||
|
|
||||||
|
buttonBar.getStyleClass().add("button-bar");
|
||||||
|
buttonBar.setVisible(!getSkinnable().getPrimaryActions().isEmpty());
|
||||||
|
buttonBar.setManaged(!getSkinnable().getPrimaryActions().isEmpty());
|
||||||
|
|
||||||
|
Bindings.bindContent(buttonBar.getButtons(), getSkinnable().getPrimaryActions());
|
||||||
|
registerListChangeListener(buttonBar.getButtons(), o -> {
|
||||||
|
buttonBar.setVisible(!getSkinnable().getPrimaryActions().isEmpty());
|
||||||
|
buttonBar.setManaged(!getSkinnable().getPrimaryActions().isEmpty());
|
||||||
|
});
|
||||||
|
|
||||||
|
// == CONTAINER ==
|
||||||
|
|
||||||
|
container.getChildren().setAll(header, buttonBar);
|
||||||
|
container.getStyleClass().add("container");
|
||||||
|
getChildren().setAll(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setMessageText() {
|
||||||
|
if (!messageText.getChildren().isEmpty()) {
|
||||||
|
messageText.getChildren().clear();
|
||||||
|
}
|
||||||
|
if (getSkinnable().getMessage() != null && !getSkinnable().getMessage().isBlank()) {
|
||||||
|
messageText.getChildren().setAll(new Text(getSkinnable().getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleClose() {
|
||||||
|
if (getSkinnable().getOnClose() != null) {
|
||||||
|
getSkinnable().getOnClose().handle(new Event(Event.ANY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double calcHeight() {
|
||||||
|
var messageHeight = messageText.getBoundsInLocal().getHeight();
|
||||||
|
|
||||||
|
return Math.max(Math.max(graphicSlot.getHeight(), actionsBox.getHeight()), messageHeight)
|
||||||
|
+ (buttonBar.isManaged() ? buttonBar.getHeight() + container.getSpacing() : 0)
|
||||||
|
+ header.getPadding().getTop()
|
||||||
|
+ header.getPadding().getBottom()
|
||||||
|
+ container.getPadding().getTop()
|
||||||
|
+ container.getPadding().getBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected double computeMinHeight(double width, double topInset, double rightInset,
|
||||||
|
double bottomInset, double leftInset) {
|
||||||
|
return calcHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
Bindings.unbindContent(actionsMenu.getItems(), getSkinnable().getSecondaryActions());
|
||||||
|
unregisterListChangeListeners(actionsMenu.getItems());
|
||||||
|
|
||||||
|
Bindings.unbindContent(buttonBar.getButtons(), getSkinnable().getPrimaryActions());
|
||||||
|
unregisterListChangeListeners(buttonBar.getButtons());
|
||||||
|
|
||||||
|
getSkinnable().graphicProperty().removeListener(graphicSlotListener);
|
||||||
|
unregisterChangeListeners(getSkinnable().messageProperty());
|
||||||
|
unregisterChangeListeners(getSkinnable().onCloseProperty());
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ public class TileSkin extends TileSkinBase<Tile> {
|
|||||||
o -> pseudoClassStateChanged(Styles.STATE_INTERACTIVE, getSkinnable().getActionHandler() != null)
|
o -> pseudoClassStateChanged(Styles.STATE_INTERACTIVE, getSkinnable().getActionHandler() != null)
|
||||||
);
|
);
|
||||||
|
|
||||||
root.setOnMouseClicked(e -> {
|
container.setOnMouseClicked(e -> {
|
||||||
if (getSkinnable().getActionHandler() != null) {
|
if (getSkinnable().getActionHandler() != null) {
|
||||||
getSkinnable().getActionHandler().run();
|
getSkinnable().getActionHandler().run();
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public abstract class TileSkinBase<T extends TileBase> extends SkinBase<T> {
|
|||||||
protected static final PseudoClass HAS_DESCRIPTION = PseudoClass.getPseudoClass("has-description");
|
protected static final PseudoClass HAS_DESCRIPTION = PseudoClass.getPseudoClass("has-description");
|
||||||
protected static final PseudoClass HAS_ACTION = PseudoClass.getPseudoClass("has-action");
|
protected static final PseudoClass HAS_ACTION = PseudoClass.getPseudoClass("has-action");
|
||||||
|
|
||||||
protected final HBox root = new HBox();
|
protected final HBox container = new HBox();
|
||||||
protected final StackPane graphicSlot;
|
protected final StackPane graphicSlot;
|
||||||
protected final ChangeListener<Node> graphicSlotListener;
|
protected final ChangeListener<Node> graphicSlotListener;
|
||||||
protected final VBox headerBox;
|
protected final VBox headerBox;
|
||||||
@ -90,17 +90,17 @@ public abstract class TileSkinBase<T extends TileBase> extends SkinBase<T> {
|
|||||||
graphicSlot.setMinWidth(Region.USE_PREF_SIZE);
|
graphicSlot.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
actionSlot.setMinWidth(Region.USE_PREF_SIZE);
|
actionSlot.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
// label text wrapping inside VBox won't work without this
|
// text wrapping inside VBox won't work without this
|
||||||
descriptionText.setMaxWidth(Region.USE_PREF_SIZE);
|
descriptionText.setMaxWidth(Region.USE_PREF_SIZE);
|
||||||
descriptionText.setMinHeight(Region.USE_PREF_SIZE);
|
descriptionText.setMinHeight(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
// do not resize children or container won't restore
|
// do not resize children or container won't restore
|
||||||
// to its original size after expanding
|
// to its original size after expanding
|
||||||
root.setFillHeight(false);
|
container.setFillHeight(false);
|
||||||
|
|
||||||
root.getChildren().setAll(graphicSlot, headerBox, actionSlot);
|
container.getChildren().setAll(graphicSlot, headerBox, actionSlot);
|
||||||
root.getStyleClass().add("container");
|
container.getStyleClass().add("container");
|
||||||
getChildren().setAll(root);
|
getChildren().setAll(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setDescriptionText() {
|
protected void setDescriptionText() {
|
||||||
@ -120,8 +120,8 @@ public abstract class TileSkinBase<T extends TileBase> extends SkinBase<T> {
|
|||||||
+ (descriptionText.isManaged() ? descriptionText.getBoundsInLocal().getHeight() : 0);
|
+ (descriptionText.isManaged() ? descriptionText.getBoundsInLocal().getHeight() : 0);
|
||||||
|
|
||||||
return Math.max(Math.max(graphicSlot.getHeight(), actionSlot.getHeight()), headerHeight)
|
return Math.max(Math.max(graphicSlot.getHeight(), actionSlot.getHeight()), headerHeight)
|
||||||
+ root.getPadding().getTop()
|
+ container.getPadding().getTop()
|
||||||
+ root.getPadding().getBottom();
|
+ container.getPadding().getBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -9,7 +9,6 @@ import atlantafx.sampler.event.DefaultEventBus;
|
|||||||
import atlantafx.sampler.event.NavEvent;
|
import atlantafx.sampler.event.NavEvent;
|
||||||
import atlantafx.sampler.page.Page;
|
import atlantafx.sampler.page.Page;
|
||||||
import atlantafx.sampler.page.components.AccordionPage;
|
import atlantafx.sampler.page.components.AccordionPage;
|
||||||
import atlantafx.sampler.page.general.AnimationsPage;
|
|
||||||
import atlantafx.sampler.page.components.BreadcrumbsPage;
|
import atlantafx.sampler.page.components.BreadcrumbsPage;
|
||||||
import atlantafx.sampler.page.components.ButtonPage;
|
import atlantafx.sampler.page.components.ButtonPage;
|
||||||
import atlantafx.sampler.page.components.CalendarPage;
|
import atlantafx.sampler.page.components.CalendarPage;
|
||||||
@ -31,6 +30,7 @@ import atlantafx.sampler.page.components.MenuBarPage;
|
|||||||
import atlantafx.sampler.page.components.MenuButtonPage;
|
import atlantafx.sampler.page.components.MenuButtonPage;
|
||||||
import atlantafx.sampler.page.components.MessagePage;
|
import atlantafx.sampler.page.components.MessagePage;
|
||||||
import atlantafx.sampler.page.components.ModalPanePage;
|
import atlantafx.sampler.page.components.ModalPanePage;
|
||||||
|
import atlantafx.sampler.page.components.NotificationPage;
|
||||||
import atlantafx.sampler.page.components.PaginationPage;
|
import atlantafx.sampler.page.components.PaginationPage;
|
||||||
import atlantafx.sampler.page.components.PopoverPage;
|
import atlantafx.sampler.page.components.PopoverPage;
|
||||||
import atlantafx.sampler.page.components.ProgressIndicatorPage;
|
import atlantafx.sampler.page.components.ProgressIndicatorPage;
|
||||||
@ -52,6 +52,7 @@ import atlantafx.sampler.page.components.ToolBarPage;
|
|||||||
import atlantafx.sampler.page.components.TooltipPage;
|
import atlantafx.sampler.page.components.TooltipPage;
|
||||||
import atlantafx.sampler.page.components.TreeTableViewPage;
|
import atlantafx.sampler.page.components.TreeTableViewPage;
|
||||||
import atlantafx.sampler.page.components.TreeViewPage;
|
import atlantafx.sampler.page.components.TreeViewPage;
|
||||||
|
import atlantafx.sampler.page.general.AnimationsPage;
|
||||||
import atlantafx.sampler.page.general.BBCodePage;
|
import atlantafx.sampler.page.general.BBCodePage;
|
||||||
import atlantafx.sampler.page.general.IconsPage;
|
import atlantafx.sampler.page.general.IconsPage;
|
||||||
import atlantafx.sampler.page.general.ThemePage;
|
import atlantafx.sampler.page.general.ThemePage;
|
||||||
@ -161,6 +162,7 @@ public class MainModel {
|
|||||||
feedback.getChildren().setAll(
|
feedback.getChildren().setAll(
|
||||||
NAV_TREE.get(DialogPage.class),
|
NAV_TREE.get(DialogPage.class),
|
||||||
NAV_TREE.get(MessagePage.class),
|
NAV_TREE.get(MessagePage.class),
|
||||||
|
NAV_TREE.get(NotificationPage.class),
|
||||||
NAV_TREE.get(ProgressIndicatorPage.class),
|
NAV_TREE.get(ProgressIndicatorPage.class),
|
||||||
NAV_TREE.get(TooltipPage.class)
|
NAV_TREE.get(TooltipPage.class)
|
||||||
);
|
);
|
||||||
@ -268,6 +270,7 @@ public class MainModel {
|
|||||||
);
|
);
|
||||||
map.put(MessagePage.class, NavTree.Item.page(MessagePage.NAME, MessagePage.class));
|
map.put(MessagePage.class, NavTree.Item.page(MessagePage.NAME, MessagePage.class));
|
||||||
map.put(ModalPanePage.class, NavTree.Item.page(ModalPanePage.NAME, ModalPanePage.class));
|
map.put(ModalPanePage.class, NavTree.Item.page(ModalPanePage.NAME, ModalPanePage.class));
|
||||||
|
map.put(NotificationPage.class, NavTree.Item.page(NotificationPage.NAME, NotificationPage.class));
|
||||||
map.put(PaginationPage.class, NavTree.Item.page(PaginationPage.NAME, PaginationPage.class));
|
map.put(PaginationPage.class, NavTree.Item.page(PaginationPage.NAME, PaginationPage.class));
|
||||||
map.put(PopoverPage.class, NavTree.Item.page(PopoverPage.NAME, PopoverPage.class));
|
map.put(PopoverPage.class, NavTree.Item.page(PopoverPage.NAME, PopoverPage.class));
|
||||||
map.put(ProgressIndicatorPage.class, NavTree.Item.page(
|
map.put(ProgressIndicatorPage.class, NavTree.Item.page(
|
||||||
|
@ -11,6 +11,7 @@ import atlantafx.sampler.page.components.DeckPanePage;
|
|||||||
import atlantafx.sampler.page.components.InputGroupPage;
|
import atlantafx.sampler.page.components.InputGroupPage;
|
||||||
import atlantafx.sampler.page.components.MessagePage;
|
import atlantafx.sampler.page.components.MessagePage;
|
||||||
import atlantafx.sampler.page.components.ModalPanePage;
|
import atlantafx.sampler.page.components.ModalPanePage;
|
||||||
|
import atlantafx.sampler.page.components.NotificationPage;
|
||||||
import atlantafx.sampler.page.components.PopoverPage;
|
import atlantafx.sampler.page.components.PopoverPage;
|
||||||
import atlantafx.sampler.page.components.TilePage;
|
import atlantafx.sampler.page.components.TilePage;
|
||||||
import atlantafx.sampler.page.components.ToggleSwitchPage;
|
import atlantafx.sampler.page.components.ToggleSwitchPage;
|
||||||
@ -39,6 +40,7 @@ record Nav(String title,
|
|||||||
InputGroupPage.class,
|
InputGroupPage.class,
|
||||||
MessagePage.class,
|
MessagePage.class,
|
||||||
ModalPanePage.class,
|
ModalPanePage.class,
|
||||||
|
NotificationPage.class,
|
||||||
PopoverPage.class,
|
PopoverPage.class,
|
||||||
TilePage.class,
|
TilePage.class,
|
||||||
ToggleSwitchPage.class
|
ToggleSwitchPage.class
|
||||||
|
@ -0,0 +1,227 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
package atlantafx.sampler.page.components;
|
||||||
|
|
||||||
|
import atlantafx.base.controls.Notification;
|
||||||
|
import atlantafx.base.theme.Styles;
|
||||||
|
import atlantafx.base.util.Animations;
|
||||||
|
import atlantafx.base.util.BBCodeParser;
|
||||||
|
import atlantafx.sampler.Resources;
|
||||||
|
import atlantafx.sampler.page.ExampleBox;
|
||||||
|
import atlantafx.sampler.page.OutlinePage;
|
||||||
|
import atlantafx.sampler.page.Snippet;
|
||||||
|
import java.net.URI;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import org.kordamp.ikonli.javafx.FontIcon;
|
||||||
|
import org.kordamp.ikonli.material2.Material2OutlinedAL;
|
||||||
|
|
||||||
|
public class NotificationPage extends OutlinePage {
|
||||||
|
|
||||||
|
public static final String NAME = "Notification";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI getJavadocUri() {
|
||||||
|
return URI.create(String.format(AFX_JAVADOC_URI_TEMPLATE, "controls/" + getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotificationPage() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
addPageHeader();
|
||||||
|
addFormattedText("""
|
||||||
|
The [i]Notification[/i] control is intended for displaying alerts and messages \
|
||||||
|
to users as pop-ups. It's customizable with different colors and icons, can contain \
|
||||||
|
a graphic or image, along with the message and additional actions for users to take."""
|
||||||
|
);
|
||||||
|
addSection("Usage", usageExample());
|
||||||
|
addSection("Actions", actionsExample());
|
||||||
|
addSection("Intent", intentExample());
|
||||||
|
addSection("Popup", popupExample());
|
||||||
|
addSection("Elevation", elevationExample());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node usageExample() {
|
||||||
|
//snippet_1:start
|
||||||
|
var ntf1 = new Notification(
|
||||||
|
FAKER.lorem().sentence(15)
|
||||||
|
);
|
||||||
|
ntf1.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
var icon1 = new ImageView(new Image(
|
||||||
|
Resources.getResourceAsStream("images/warning_32.png")
|
||||||
|
));
|
||||||
|
ntf1.setGraphic(icon1);
|
||||||
|
|
||||||
|
var ntf2 = new Notification(FAKER.lorem().sentence(15));
|
||||||
|
ntf2.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
ntf2.setOnClose(e -> Animations.flash(ntf2).playFromStart());
|
||||||
|
//snippet_1:end
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, ntf1, ntf2);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]Notification[/i] has no mandatory properties, but it wouldn't make \
|
||||||
|
sense without a message. Optionally, you can use a graphic and also make the \
|
||||||
|
[i]Notification[/i] closeable by providing an appropriate dismiss handler."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 1), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node actionsExample() {
|
||||||
|
//snippet_2:start
|
||||||
|
var ntf = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
ntf.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
ntf.setOnClose(e -> Animations.flash(ntf).playFromStart());
|
||||||
|
|
||||||
|
var yesBtn = new Button("Yes");
|
||||||
|
yesBtn.setDefaultButton(true);
|
||||||
|
|
||||||
|
var noBtn = new Button("No");
|
||||||
|
|
||||||
|
ntf.setPrimaryActions(yesBtn, noBtn);
|
||||||
|
ntf.setSecondaryActions(
|
||||||
|
new MenuItem("Item 1"),
|
||||||
|
new MenuItem("Item 2")
|
||||||
|
);
|
||||||
|
//snippet_2:end
|
||||||
|
|
||||||
|
var box = new VBox(ntf);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The notification has two slots for setting custom actions. The primary action \
|
||||||
|
slot appears like a [i]ButtonBar[/i] (check the Javadoc for its features, by the way) \
|
||||||
|
at the bottom of the notification. The secondary actions slot is the dropdown menu \
|
||||||
|
at the top right corner. Both slots are completely optional."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 2), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node intentExample() {
|
||||||
|
//snippet_3:start
|
||||||
|
var info = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
info.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
info.getStyleClass().add(Styles.ACCENT);
|
||||||
|
info.setOnClose(e -> Animations.flash(info).playFromStart());
|
||||||
|
|
||||||
|
var success = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
success.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
success.getStyleClass().add(Styles.SUCCESS);
|
||||||
|
success.setOnClose(e -> Animations.flash(success).playFromStart());
|
||||||
|
|
||||||
|
var warning = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
warning.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
warning.getStyleClass().add(Styles.WARNING);
|
||||||
|
warning.setOnClose(e -> Animations.flash(warning).playFromStart());
|
||||||
|
|
||||||
|
var danger = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
danger.getStyleClass().add(Styles.ELEVATED_1);
|
||||||
|
danger.getStyleClass().add(Styles.DANGER);
|
||||||
|
danger.setOnClose(e -> Animations.flash(danger).playFromStart());
|
||||||
|
//snippet_3:end
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, info, success, warning, danger);
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
The [i]Notification[/i] supports colors (or intents). To set them, \
|
||||||
|
use the corresponding style class modifier."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 3), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExampleBox popupExample() {
|
||||||
|
//snippet_4:start
|
||||||
|
final var msg = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
msg.getStyleClass().addAll(
|
||||||
|
Styles.ACCENT, Styles.ELEVATED_1
|
||||||
|
);
|
||||||
|
msg.setPrefHeight(Region.USE_PREF_SIZE);
|
||||||
|
msg.setMaxHeight(Region.USE_PREF_SIZE);
|
||||||
|
StackPane.setAlignment(msg, Pos.TOP_RIGHT);
|
||||||
|
StackPane.setMargin(msg, new Insets(10, 10, 0, 0));
|
||||||
|
|
||||||
|
var btn = new Button("Show");
|
||||||
|
|
||||||
|
msg.setOnClose(e -> {
|
||||||
|
var out = Animations.slideOutUp(msg, Duration.millis(250));
|
||||||
|
out.setOnFinished(f -> getChildren().remove(msg));
|
||||||
|
out.playFromStart();
|
||||||
|
});
|
||||||
|
btn.setOnAction(e -> {
|
||||||
|
var in = Animations.slideInDown(msg, Duration.millis(250));
|
||||||
|
if (!getChildren().contains(msg)) {
|
||||||
|
getChildren().add(msg);
|
||||||
|
}
|
||||||
|
in.playFromStart();
|
||||||
|
});
|
||||||
|
//snippet_4:end
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, btn);
|
||||||
|
box.setPadding(new Insets(0, 0, 10, 0));
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
There isn't any special support for popup notifications, but \
|
||||||
|
you can easily implement it by using the [i]StackPane[/i] layout."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 4), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node elevationExample() {
|
||||||
|
//snippet_5:start
|
||||||
|
var ntf1 = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
ntf1.getStyleClass().add(Styles.ELEVATED_2);
|
||||||
|
ntf1.setOnClose(e -> Animations.flash(ntf1).playFromStart());
|
||||||
|
|
||||||
|
var ntf2 = new Notification(
|
||||||
|
FAKER.lorem().sentence(15),
|
||||||
|
new FontIcon(Material2OutlinedAL.HELP_OUTLINE)
|
||||||
|
);
|
||||||
|
ntf2.getStyleClass().add(Styles.INTERACTIVE);
|
||||||
|
ntf2.setOnClose(e -> Animations.flash(ntf2).playFromStart());
|
||||||
|
|
||||||
|
//snippet_5:end
|
||||||
|
|
||||||
|
var box = new VBox(VGAP_20, ntf1, ntf2);
|
||||||
|
box.setPadding(new Insets(0, 0, 10, 0));
|
||||||
|
var description = BBCodeParser.createFormattedText("""
|
||||||
|
To add the raised effect to the [i]Notification[/i], use the [code]Styles.ELEVATED_N[/code] \
|
||||||
|
or [code]Styles.INTERACTIVE[/code] style classes."""
|
||||||
|
);
|
||||||
|
|
||||||
|
return new ExampleBox(box, new Snippet(getClass(), 5), description);
|
||||||
|
}
|
||||||
|
}
|
BIN
sampler/src/main/resources/atlantafx/sampler/images/info_32.png
Normal file
BIN
sampler/src/main/resources/atlantafx/sampler/images/info_32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
@ -19,6 +19,7 @@
|
|||||||
@use "menu-button";
|
@use "menu-button";
|
||||||
@use "message";
|
@use "message";
|
||||||
@use "modal-pane";
|
@use "modal-pane";
|
||||||
|
@use "notification";
|
||||||
@use "pagination";
|
@use "pagination";
|
||||||
@use "popover";
|
@use "popover";
|
||||||
@use "progress";
|
@use "progress";
|
||||||
|
@ -38,9 +38,8 @@ $color-border-danger: -color-danger-muted !default;
|
|||||||
$color-border-danger-hover: -color-danger-emphasis !default;
|
$color-border-danger-hover: -color-danger-emphasis !default;
|
||||||
$color-button-danger-bg-hover: -color-danger-muted !default;
|
$color-button-danger-bg-hover: -color-danger-muted !default;
|
||||||
|
|
||||||
$close-button-radius: 100px !default;
|
$close-button-radius: 100px !default;
|
||||||
$close-button-padding: 0.6em !default;
|
$close-button-padding: 0.5em; // private variable
|
||||||
$close-button-icon-size: 0.3em !default;
|
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
|
|
||||||
@ -147,11 +146,12 @@ $close-button-icon-size: 0.3em !default;
|
|||||||
>.icon {
|
>.icon {
|
||||||
@include icons.get("close", true);
|
@include icons.get("close", true);
|
||||||
-fx-background-color: -color-message-fg-primary;
|
-fx-background-color: -color-message-fg-primary;
|
||||||
-fx-padding: $close-button-icon-size;
|
-fx-padding: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
-fx-background-color: -color-message-button-hover;
|
-fx-background-color: -color-message-border, -color-message-button-hover;
|
||||||
|
-fx-background-insets: 0, 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ $color-modal-bg: rgba(0, 0, 0, 0.25) !default;
|
|||||||
$color-dialog-bg: -color-bg-default !default;
|
$color-dialog-bg: -color-bg-default !default;
|
||||||
$color-dialog-close-fg: -color-fg-default !default;
|
$color-dialog-close-fg: -color-fg-default !default;
|
||||||
$color-dialog-close-bg-hover: -color-bg-subtle !default;
|
$color-dialog-close-bg-hover: -color-bg-subtle !default;
|
||||||
$close-button-radius: 100px !default;
|
$close-button-radius: 100px !default;
|
||||||
$close-button-padding: 0.6em !default;
|
$close-button-padding: 0.6em !default;
|
||||||
$close-button-icon-size: 0.3em !default;
|
$close-button-icon-size: 0.3em !default;
|
||||||
|
|
||||||
.modal-pane {
|
.modal-pane {
|
||||||
@ -38,7 +38,8 @@ $close-button-icon-size: 0.3em !default;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
-fx-background-color: -color-modal-box-close-bg-hover;
|
-fx-background-color: -color-border-muted, -color-modal-box-close-bg-hover;
|
||||||
|
-fx-background-insets: 0, 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
159
styles/src/components/_notification.scss
Normal file
159
styles/src/components/_notification.scss
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
@use "../settings/config" as cfg;
|
||||||
|
@use "../settings/effects";
|
||||||
|
@use "../settings/icons";
|
||||||
|
|
||||||
|
$color-bg: -color-bg-subtle !default;
|
||||||
|
$color-fg: -color-fg-default !default;
|
||||||
|
$color-bg-hover: -color-bg-default !default;
|
||||||
|
$color-fg-hover: -color-fg-default !default;
|
||||||
|
$color-border: -color-border-default !default;
|
||||||
|
$color-border-intent: -color-accent-emphasis !default;
|
||||||
|
|
||||||
|
$padding-x: 1em !default;
|
||||||
|
$padding-y: 1em !default;
|
||||||
|
$graphic-text-gap: 0.75em !default;
|
||||||
|
$intent-border-width: 5px !default;
|
||||||
|
$action-button-radius: 100px !default;
|
||||||
|
|
||||||
|
@mixin colored($color-intent) {
|
||||||
|
-color-notify-border-intent: $color-intent;
|
||||||
|
|
||||||
|
&>.container {
|
||||||
|
-fx-background-color: -color-notify-border,
|
||||||
|
-color-notify-bg,
|
||||||
|
-color-notify-border-intent -color-notify-bg -color-notify-bg -color-notify-bg,
|
||||||
|
-color-notify-bg;
|
||||||
|
-fx-background-insets: 0, 1, 1, 1 1 1 (1 + $intent-border-width);
|
||||||
|
-fx-background-radius: cfg.$border-radius;
|
||||||
|
|
||||||
|
>.header {
|
||||||
|
>.graphic {
|
||||||
|
#{cfg.$font-icon-selector} {
|
||||||
|
-fx-fill: $color-intent;
|
||||||
|
-fx-icon-color: $color-intent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
-color-notify-bg: $color-bg;
|
||||||
|
-color-notify-fg: $color-fg;
|
||||||
|
-color-notify-bg-hover: $color-bg-hover;
|
||||||
|
-color-notify-fg-hover: $color-fg-hover;
|
||||||
|
-color-notify-border: $color-border;
|
||||||
|
-color-notify-border-intent: $color-border-intent;
|
||||||
|
|
||||||
|
>.container {
|
||||||
|
-fx-background-color: -color-notify-border, -color-notify-bg;
|
||||||
|
-fx-background-insets: 0, cfg.$border-width;
|
||||||
|
-fx-background-radius: cfg.$border-radius;
|
||||||
|
-fx-spacing: $padding-y;
|
||||||
|
-fx-padding: 0 0 $padding-y 0;
|
||||||
|
|
||||||
|
>.header {
|
||||||
|
-fx-padding: $padding-y $padding-x 0 $padding-x;
|
||||||
|
-fx-spacing: $graphic-text-gap;
|
||||||
|
|
||||||
|
>.graphic {
|
||||||
|
#{cfg.$font-icon-selector} {
|
||||||
|
-fx-icon-size: cfg.$icon-size-larger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.message {
|
||||||
|
Text {
|
||||||
|
-fx-fill: -color-notify-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.actions {
|
||||||
|
-fx-alignment: CENTER_RIGHT;
|
||||||
|
-fx-spacing: 5px;
|
||||||
|
|
||||||
|
>.secondary-menu-button {
|
||||||
|
-fx-background-radius: $action-button-radius;
|
||||||
|
-fx-padding: 0.5em 0.75em 0.5em 0.75em;
|
||||||
|
|
||||||
|
>.icon {
|
||||||
|
@include icons.get("more-vert", true);
|
||||||
|
-fx-background-color: -color-notify-fg;
|
||||||
|
-fx-background-insets: 0;
|
||||||
|
-fx-padding: 0.3em 0.1em 0.3em 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
-fx-background-color: -color-border-default, -color-notify-bg-hover;
|
||||||
|
-fx-background-insets: 0, 1;
|
||||||
|
|
||||||
|
>.icon {
|
||||||
|
-fx-background-color: -color-notify-fg-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
>.close-button {
|
||||||
|
-fx-background-radius: $action-button-radius;
|
||||||
|
-fx-padding: 0.5em;
|
||||||
|
|
||||||
|
>.icon {
|
||||||
|
@include icons.get("close", true);
|
||||||
|
-fx-background-color: -color-notify-fg;
|
||||||
|
-fx-padding: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
-fx-background-color: -color-border-default, -color-notify-bg-hover;
|
||||||
|
-fx-background-insets: 0, 1;
|
||||||
|
|
||||||
|
>.icon {
|
||||||
|
-fx-background-color: -color-notify-fg-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
-fx-opacity: cfg.$opacity-disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
>.button-bar {
|
||||||
|
-fx-padding: 0 $padding-x 0 $padding-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $level, $radius in cfg.$elevation {
|
||||||
|
&.elevated-#{$level} {
|
||||||
|
>.container {
|
||||||
|
@include effects.shadow(cfg.$elevation-color, $radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.interactive:hover {
|
||||||
|
>.container {
|
||||||
|
@include effects.shadow(cfg.$elevation-color, cfg.$elevation-interactive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.accent {
|
||||||
|
@include colored(-color-accent-emphasis);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
@include colored(-color-success-emphasis);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
@include colored(-color-warning-emphasis);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
@include colored(-color-danger-emphasis);
|
||||||
|
}
|
||||||
|
}
|
@ -153,6 +153,18 @@ $nord16: #B48EAD; // hsl(311, 20.2, 63.1)
|
|||||||
$color-menubar-bg-hover: -color-base-7
|
$color-menubar-bg-hover: -color-base-7
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@forward "components/message" as message-* with (
|
||||||
|
$close-button-radius: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
@forward "components/modal-pane" as modal-pane-* with (
|
||||||
|
$close-button-radius: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
@forward "components/notification" as notification-* with (
|
||||||
|
$action-button-radius: 0
|
||||||
|
);
|
||||||
|
|
||||||
@forward "components/pagination" as pagination-* with (
|
@forward "components/pagination" as pagination-* with (
|
||||||
$button-radius: 1
|
$button-radius: 1
|
||||||
);
|
);
|
||||||
|
@ -139,6 +139,18 @@ $nord16: #B48EAD; // hsl(311, 20.2, 63.1)
|
|||||||
|
|
||||||
@use "general";
|
@use "general";
|
||||||
|
|
||||||
|
@forward "components/message" as message-* with (
|
||||||
|
$close-button-radius: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
@forward "components/modal-pane" as modal-pane-* with (
|
||||||
|
$close-button-radius: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
@forward "components/notification" as notification-* with (
|
||||||
|
$action-button-radius: 0
|
||||||
|
);
|
||||||
|
|
||||||
@forward "components/pagination" as pagination-* with (
|
@forward "components/pagination" as pagination-* with (
|
||||||
$button-radius: 1
|
$button-radius: 1
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user