org.dominokit.domino.ui.sliders.Slider Maven / Gradle / Ivy
/*
* Copyright © 2019 Dominokit
*
* 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.
*/
package org.dominokit.domino.ui.sliders;
import static org.dominokit.domino.ui.events.EventType.blur;
import static org.dominokit.domino.ui.events.EventType.change;
import static org.dominokit.domino.ui.events.EventType.mousedown;
import static org.dominokit.domino.ui.events.EventType.mousemove;
import static org.dominokit.domino.ui.events.EventType.mouseout;
import static org.dominokit.domino.ui.events.EventType.mouseup;
import static org.dominokit.domino.ui.events.EventType.touchend;
import static org.dominokit.domino.ui.events.EventType.touchmove;
import static org.dominokit.domino.ui.events.EventType.touchstart;
import static org.dominokit.domino.ui.utils.Domino.*;
import elemental2.dom.EventListener;
import elemental2.dom.HTMLDivElement;
import java.util.HashSet;
import java.util.Set;
import org.dominokit.domino.ui.elements.DivElement;
import org.dominokit.domino.ui.elements.InputElement;
import org.dominokit.domino.ui.elements.SpanElement;
import org.dominokit.domino.ui.events.EventOptions;
import org.dominokit.domino.ui.events.EventType;
import org.dominokit.domino.ui.utils.BaseDominoElement;
import org.dominokit.domino.ui.utils.ChildHandler;
import org.dominokit.domino.ui.utils.HasChangeListeners;
/**
* Represents a UI slider component.
*
* The slider allows users to select a value from a range by moving the slider thumb.
*
*
* Usage example:
*
* Slider slider = Slider.create(100, 0, 50); // max: 100, min: 0, initial value: 50
* slider.addChangeListener(value -> {
* System.out.println("New value: " + value);
* });
*
*
* @see BaseDominoElement
*/
public class Slider extends BaseDominoElement
implements HasChangeListeners, SliderStyles {
private final DivElement root;
private final InputElement input;
private final SpanElement thumb;
private final SpanElement valueElement;
private double oldValue;
private Set> changeListeners = new HashSet<>();
private boolean mouseDown;
private boolean withThumb;
private boolean changeListenersPaused;
/**
* Creates a slider with a specified maximum value.
*
* @param max the maximum value for the slider
* @return a new slider instance
*/
public static Slider create(double max) {
return create(max, 0, 0);
}
/**
* Creates a slider with a specified maximum and minimum value.
*
* @param max the maximum value for the slider
* @param min the minimum value for the slider
* @return a new slider instance
*/
public static Slider create(double max, double min) {
return create(max, min, 0);
}
/**
* Creates a slider with specified maximum, minimum, and initial value.
*
* @param max the maximum value for the slider
* @param min the minimum value for the slider
* @param value the initial value for the slider
* @return a new slider instance
*/
public static Slider create(double max, double min, double value) {
return new Slider(max, min, value);
}
/**
* Main constructor to create a Slider.
*
* @param max the maximum value
* @param min the minimum value
* @param value the initial value
*/
public Slider(double max, double min, double value) {
root =
div()
.addCss(dui_slider)
.appendChild(
input = input("range").addCss(dui_slider_input).setAttribute("step", "any"))
.appendChild(
thumb =
span()
.addCss(dui_slider_thumb)
.collapse()
.appendChild(valueElement = span().addCss(dui_slider_value)));
setMaxValue(max);
setMinValue(min);
setValue(value);
EventListener downEvent =
mouseDownEvent -> {
this.oldValue = getValue();
input.addCss(dui_active);
this.mouseDown = true;
if (withThumb) {
showThumb();
evaluateThumbPosition();
}
};
EventListener moveMouseListener =
mouseMoveEvent -> {
if (mouseDown) {
if (withThumb) {
evaluateThumbPosition();
updateThumbValue();
}
}
};
EventListener leaveMouseListener = evt -> hideThumb();
EventListener upEvent =
mouseUpEvent -> {
mouseDown = false;
input.removeCss(dui_active);
hideThumb();
};
input.addEventListener(change.getName(), evt -> triggerChangeListeners(oldValue, getValue()));
input.addEventListener(mousedown.getName(), downEvent);
input.addEventListener(touchstart.getName(), downEvent, EventOptions.of().setPassive(true));
input.addEventListener(mousemove.getName(), moveMouseListener);
input.addEventListener(
touchmove.getName(), moveMouseListener, EventOptions.of().setPassive(true));
input.addEventListener(EventType.input.getName(), moveMouseListener);
input.addEventListener(mouseup.getName(), upEvent);
input.addEventListener(touchend.getName(), upEvent, EventOptions.of().setPassive(true));
input.addEventListener(mouseout.getName(), leaveMouseListener);
input.addEventListener(blur.getName(), leaveMouseListener);
init(this);
}
/**
* Calculates the range offset of the slider's thumb based on its current value.
*
* @return the calculated range offset in pixels
*/
private double calculateRangeOffset() {
int width = input.element().offsetWidth - 15;
double percent = (getValue() - getMin()) / (getMax() - getMin());
return percent * width + input.element().offsetLeft;
}
/**
* Pauses the change listeners so they won't get triggered on value changes.
*
* @return the current slider instance
*/
@Override
public Slider pauseChangeListeners() {
this.changeListenersPaused = true;
return this;
}
/**
* Resumes the change listeners so they get triggered on value changes.
*
* @return the current slider instance
*/
@Override
public Slider resumeChangeListeners() {
this.changeListenersPaused = false;
return this;
}
/**
* Toggles the pause state of the change listeners.
*
* @param toggle if true, pause the listeners, otherwise resume them
* @return the current slider instance
*/
@Override
public Slider togglePauseChangeListeners(boolean toggle) {
this.changeListenersPaused = toggle;
return this;
}
/**
* Gets the set of change listeners attached to the slider.
*
* @return the set of change listeners
*/
@Override
public Set> getChangeListeners() {
return changeListeners;
}
/**
* Checks if the change listeners are currently paused.
*
* @return true if listeners are paused, false otherwise
*/
@Override
public boolean isChangeListenersPaused() {
return this.changeListenersPaused;
}
/**
* Triggers the change listeners manually with given old and new values.
*
* @param oldValue the previous value
* @param newValue the current value
* @return the current slider instance
*/
@Override
public Slider triggerChangeListeners(Double oldValue, Double newValue) {
if (!isChangeListenersPaused()) {
changeListeners.forEach(changeListener -> changeListener.onValueChanged(oldValue, newValue));
}
return this;
}
/** Updates the display value of the slider's thumb based on its current value. */
private void updateThumbValue() {
if (withThumb) {
valueElement.setTextContent(String.valueOf(Double.valueOf(getValue()).intValue()));
}
}
/** Shows the slider's thumb and updates its value display. */
private void showThumb() {
thumb.expand();
updateThumbValue();
}
/** Hides the slider's thumb. */
private void hideThumb() {
thumb.collapse();
}
/**
* Configures the slider to show its thumb and applies additional customizations using the
* provided handler.
*
* @param handler a handler that allows customizations of the slider's thumb
* @return the current slider instance
*/
public Slider withThumb(ChildHandler handler) {
setShowThumb(true);
handler.apply(this, thumb);
return this;
}
/**
* Configures the slider to show its thumb without any additional customizations.
*
* @return the current slider instance
*/
public Slider withThumb() {
setShowThumb(true);
return this;
}
/** Evaluates the position of the slider's thumb based on the current value of the slider. */
private void evaluateThumbPosition() {
if (mouseDown) {
thumb.style().setLeft(calculateRangeOffset() + "px");
}
}
/**
* Sets the maximum value of the slider.
*
* @param max the maximum value to be set
* @return the current slider instance
*/
public Slider setMaxValue(double max) {
input.element().max = String.valueOf(max);
return this;
}
/**
* Sets the minimum value of the slider.
*
* @param min the minimum value to be set
* @return the current slider instance
*/
public Slider setMinValue(double min) {
input.element().min = String.valueOf(min);
return this;
}
/**
* Sets the value of the slider and optionally triggers change listeners.
*
* @param newValue the new value to be set
* @param silent if true, change listeners won't be triggered
* @return the current slider instance
*/
public Slider setValue(double newValue, boolean silent) {
double oldValue = getValue();
input.element().value = String.valueOf(newValue);
updateThumbValue();
if (!silent) {
triggerChangeListeners(oldValue, newValue);
}
return this;
}
/**
* Sets the value of the slider and triggers change listeners.
*
* @param newValue the new value to be set
* @return the current slider instance
*/
public Slider setValue(double newValue) {
return setValue(newValue, false);
}
/**
* Sets the stepping value of the slider.
*
* @param step the stepping value to be set
* @return the current slider instance
*/
public Slider setStep(double step) {
input.element().step = String.valueOf(step);
return this;
}
/**
* Configures the slider to accept any step value.
*
* @return the current slider instance
*/
public Slider anyStep() {
input.element().step = "any";
return this;
}
/**
* Gets the maximum value of the slider.
*
* @return the maximum value
*/
public double getMax() {
return Double.parseDouble(input.element().max);
}
/**
* Gets the minimum value of the slider.
*
* @return the minimum value
*/
public double getMin() {
return Double.parseDouble(input.element().min);
}
/**
* Gets the current value of the slider.
*
* @return the current value
*/
public double getValue() {
return input.element().valueAsNumber;
}
/**
* Sets whether the slider should show its thumb.
*
* @param withThumb if true, the thumb will be shown
* @return the current slider instance
*/
public Slider setShowThumb(boolean withThumb) {
this.withThumb = withThumb;
return this;
}
/**
* Returns the root HTML div element of the slider.
*
* @return the root HTML div element
*/
@Override
public HTMLDivElement element() {
return root.element();
}
/**
* Adds a change listener to the slider. This listener will be notified of value changes.
*
* @param changeListener the listener to be added
* @return the current slider instance
*/
@Override
public Slider addChangeListener(ChangeListener super Double> changeListener) {
changeListeners.add(changeListener);
return this;
}
/**
* Removes a specific change listener from the slider.
*
* @param changeListener the listener to be removed
* @return the current slider instance
*/
@Override
public Slider removeChangeListener(ChangeListener super Double> changeListener) {
changeListeners.remove(changeListener);
return this;
}
/**
* Checks if the slider has the specified change listener.
*
* @param changeListener the listener to check for
* @return true if the listener is present, false otherwise
*/
@Override
public boolean hasChangeListener(ChangeListener super Double> changeListener) {
return changeListeners.contains(changeListener);
}
/**
* A functional interface to handle slider slide events.
*
* @author ChatGPT
*/
@FunctionalInterface
public interface SlideHandler {
/**
* Called when the slider value changes.
*
* @param value the new value of the slider
*/
void onSlide(double value);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy