From 84c37c1386960df932aeb4eabc4f41be6dbbacd3 Mon Sep 17 00:00:00 2001 From: Jin Park <61643387+lemonade5117@users.noreply.github.com> Date: Thu, 5 Nov 2020 20:33:21 +0900 Subject: [PATCH] Slider that displays lower and upper bound (#63) Co-authored-by: fonsp --- Project.toml | 2 +- assets/rangeslider.css | 87 ++++++++++++++++++++++++++++++++++++++++++ assets/rangeslider.js | 81 +++++++++++++++++++++++++++++++++++++++ src/PlutoUI.jl | 1 + src/RangeSlider.jl | 64 +++++++++++++++++++++++++++++++ 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 assets/rangeslider.css create mode 100644 assets/rangeslider.js create mode 100644 src/RangeSlider.jl diff --git a/Project.toml b/Project.toml index a592b434..d54161c1 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PlutoUI" uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8" authors = ["Fons van der Plas "] -version = "0.6.9" +version = "0.6.10" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/assets/rangeslider.css b/assets/rangeslider.css new file mode 100644 index 00000000..6bb5ce54 --- /dev/null +++ b/assets/rangeslider.css @@ -0,0 +1,87 @@ +.middle { + position: relative; + width: 50%; + max-width: 500px; +} + +.slider { + position: relative; + z-index: 1; + height: 10px; + margin: 15px 15px; +} + +.slider > .track { + position: absolute; + z-index: 1; + left: 0; + right: 0; + top: 0; + bottom: 0; + border-radius: 5px; + border: 1px solid rgb(182, 182, 182); + background-color: rgb(239, 239, 239); +} + +.slider > .range { + position: absolute; + z-index: 2; + top: 0; + bottom: 0; + border-radius: 5px; + border: 1px solid rgb(69, 119, 184); + background-color: rgb(20, 121, 251); +} + +.slider > .range:hover { + filter: brightness(90%); +} + +.slider > .track:hover { + filter: brightness(95%); +} + +.slider > .thumb { + position: absolute; + z-index: 3; + width: 15px; + height: 15px; + background-color: rgb(20, 121, 251); + border-radius: 50%; +} + +.slider > .thumb.left { + transform: translate(-7.5px, -2.5px); +} + +.slider > .thumb.right { + transform: translate(7.5px, -2.5px); +} + +#input-left, #input-right { + position: absolute; + pointer-events: none; + -webkit-appearance: none; + z-index: 2; + height: 10px; + width: 100%; + opacity: 0; +} + +#input-left::-moz-range-thumb, #input-right::-moz-range-thumb { + pointer-events: all; + width: 20px; + height: 20px; + border-radius: 0; + border: 0 none; + -moz-appearance: none; +} + +#input-left::-webkit-slider-thumb, #input-right::-webkit-slider-thumb { + pointer-events: all; + width: 20px; + height: 20px; + border-radius: 0; + border: 0 none; + -webkit-appearance: none; +} \ No newline at end of file diff --git a/assets/rangeslider.js b/assets/rangeslider.js new file mode 100644 index 00000000..63580a59 --- /dev/null +++ b/assets/rangeslider.js @@ -0,0 +1,81 @@ +const container = currentScript.parentElement +const inputLeft = container.querySelector("#input-left"); +const inputRight = container.querySelector("#input-right"); +const thumbLeft = container.querySelector(".slider > .thumb.left"); +const thumbRight = container.querySelector(".slider > .thumb.right"); +const range = container.querySelector(".slider > .range"); +const display = container.querySelector("#slider-output") + +const min = parseFloat(inputLeft.min); +const max = parseFloat(inputLeft.max); + +function setLeftValue() { + inputLeft.value = Math.min(parseFloat(inputLeft.value), parseFloat(inputRight.value)); + + const percent = ((inputLeft.value - min) / (max - min)) * 100; + + thumbLeft.style.left = percent + "%"; + range.style.left = percent + "%"; + + const leftValue = parseFloat(inputLeft.value), + rightValue = parseFloat(inputRight.value); + + if (rightValue === leftValue) { + inputRight.hidden = true; + } else { + inputRight.hidden = false; + } + + var returnValue = [] + var i = leftValue; + while (i <= rightValue) { + returnValue.push(i); + i += parseFloat(inputLeft.step); + } + + container.value = returnValue; + container.dispatchEvent(new CustomEvent("input")); +} +setLeftValue(); + +function setRightValue() { + inputRight.value = Math.max(parseFloat(inputRight.value), parseFloat(inputLeft.value)); + + const percent = ((inputRight.value - min) / (max - min)) * 100; + + thumbRight.style.right = (100 - percent) + "%"; + range.style.right = (100 - percent) + "%"; + + const leftValue = parseFloat(inputLeft.value), + rightValue = parseFloat(inputRight.value); + + if(rightValue === leftValue) { + inputLeft.hidden = true; + } else { + inputLeft.hidden = false; + } + + var returnValue = [] + var i = leftValue; + while (i <= rightValue) { + returnValue.push(i); + i += parseFloat(inputLeft.step); + } + + container.value = returnValue; + container.dispatchEvent(new CustomEvent("input")); +} +setRightValue(); + +inputLeft.addEventListener("input", setLeftValue); +inputRight.addEventListener("input", setRightValue); + +function updateDisplay() { + if(display != null) { + display.value = (inputLeft.step == 1) ? `${inputLeft.value}:${inputRight.value}` + : `${inputLeft.value}:${inputLeft.step}:${inputRight.value}`; + } +} +inputLeft.addEventListener("input", updateDisplay) +inputRight.addEventListener("input", updateDisplay) +updateDisplay() \ No newline at end of file diff --git a/src/PlutoUI.jl b/src/PlutoUI.jl index 9c7cdd60..d1669181 100644 --- a/src/PlutoUI.jl +++ b/src/PlutoUI.jl @@ -9,6 +9,7 @@ include("./Builtins.jl") include("./Clock.jl") include("./Resource.jl") include("./Terminal.jl") +include("./RangeSlider.jl") include("./DisplayTricks.jl") end diff --git a/src/RangeSlider.jl b/src/RangeSlider.jl new file mode 100644 index 00000000..68b9e886 --- /dev/null +++ b/src/RangeSlider.jl @@ -0,0 +1,64 @@ +export RangeSlider + +struct RangeSlider + range::AbstractRange + left::Number + right::Number + show_value::Bool +end + +RangeSlider(range::AbstractRange; left=first(range), right=last(range), show_value=false) = +left > right ? error("Left value cannot be bigger than right") : RangeSlider(range, left, right, show_value) + +""" +A `RangeSlider` is a two thumb slider which returns the range between the two thumbs. + +If you set `show_value` to `false`, the slider won't display its value. + +By default `show_value` is set to `true` + +Use `left` and `right` to set default values for the slider thumbs. + +## Examples +`@bind range RangeSlider(1:100)` + +`@bind range RangeSlider(1:0.1:100, show_value=false)` + +`@bind range RangeSlider(1:0.1:100, left=25, right=75)` +""" + +function show(io::IO, ::MIME"text/html", slider::RangeSlider) + js = read(joinpath(PKG_ROOT_DIR, "assets", "rangeslider.js"), String) + css = read(joinpath(PKG_ROOT_DIR, "assets", "rangeslider.css"), String) + + result = """ + +
+ + + +
+
+
+
+
+
+
+ $(slider.show_value ? """""" : "") + + +
+ """ + + print(io, result) +end + +get(slider::RangeSlider) = collect(slider.left:step(slider.range):slider.right) \ No newline at end of file