1import define1 from "./[email protected]"; 2 3function _1(md){return( 4md`# Range Slider 5 6**Jump to: 7[Examples](#section_examples) | [Documentation](#section_docs) | [Themes](#section_themes) | [Changelog](#doc_changelog)** 8 9This notebook implements a themable range input for lower and upper bounds. 10 11<details> 12<summary style="outline:none !important"><i>View a demonstration</i></summary> 13<img src=https://i.imgur.com/KkHoemh.gif style="max-width:500px;border:1px solid #ddd"> 14</details> 15` 16)} 17 18function _section_examples(md){return( 19md`--- 20## Examples 21 22*All* arguments and options are optional.` 23)} 24 25function _example_observable(SLUG,md){return( 26md`### Style: Observable Inputs 27 28Mimics the appearance and interface of \`Inputs.range()\` from Observable's [Inputs library](https://observablehq.com/@observablehq/inputs). See the [documentation](#doc_interval) for a list of all available options. 29 30~~~js 31import {interval} from '${SLUG}' 32~~~ 33` 34)} 35 36function _4(interval){return( 37interval([-10, 10], { 38 step: .25, 39 value: [0, 8], 40 label: 'My slider', 41}) 42)} 43 44function _example_jashkenas(SLUG,md){return( 45md`--- 46### Style: Jeremy Ashkenas' Inputs 47 48***This widget has been [deprecated](https://en.wikipedia.org/wiki/Deprecation).*** *Please use [\`interval()\`](#example_observable) instead.* 49 50 51Mimics the appearance and interface of \`slider()\` from Jeremy Ashkenas' [Inputs notebook](https://observablehq.com/@jashkenas/inputs). See the [documentation](#doc_rangeSlider) for a list of all available options. 52 53~~~js 54import {rangeSlider} from '${SLUG}' 55~~~ 56` 57)} 58 59function _6(rangeSlider){return( 60rangeSlider({ 61 min: -10, 62 max: 10, 63 step: .25, 64 value: [0, 8], 65 title: 'My slider', 66 description: 'This is a slider.', 67}) 68)} 69 70function _example_basic(SLUG,md){return( 71md`--- 72### Style: Basic 73 74The basic building block for custom sliders. See the [documentation](#doc_rangeInput) for a list of all available options. 75 76~~~js 77import {rangeInput} from '${SLUG}' 78~~~ 79` 80)} 81 82function _8(rangeInput){return( 83rangeInput({ 84 min: -10, 85 max: 10, 86 step: .25, 87 value: [0, 8], 88}) 89)} 90 91function _section_docs(md){return( 92md`--- 93## Documentation` 94)} 95 96function _doc_interval(signature,interval,md){return( 97signature(interval, { 98 description: md` 99Widget that imitates the appearance and API of [\`range()\`](https://observablehq.com/@observablehq/input-range). 100 101Arguments: 102- \`range\`: Array of \`[min, max]\`. Defaults to \`[0, 1]\`. 103- \`options\`: Optional. Available options: 104${Object.entries({ 105 min: 'Minimum value, number. Defaults to <code>0</code>.', 106 max: 'Maximum value, number. Defaults to <code>100</code>.', 107 step: 'Range step, number. Defaults to <code>any</code>.', 108 label: 'Input label. Defaults to `null`.', 109 format: 'Callback that receives the current value and returns an output string. Defaults to ```([start, end]) => `${start} ⦠${end}` ```.', 110 value: 'Lower and upper range bounds, array of number. Optional, defaults to <code>[min, max]</code>.', 111 theme: 'Widget CSS, string. Defaults to [<code>theme_Flat</code>](#doc_theme_Flat).', 112 color: 'Range color as CSS \`color\` value. Defaults to the theme.', 113 width: 'Range width, either number of pixels or a CSS \`width\` string. Defaults to the theme.', 114}).map(([k,v]) => ` - \`${k}:\` ${v}\n`)} 115 116` 117}) 118)} 119 120function _doc_rangeInput(signature,rangeInput,md){return( 121signature(rangeInput, { 122 description: md` 123Basic input implementation. Supports custom themes. 124 125Arguments: 126- \`options:\` Optional. Available options: 127${Object.entries({ 128 min: 'Minimum value, number. Defaults to <code>0</code>.', 129 max: 'Maximum value, number. Defaults to <code>100</code>.', 130 step: 'Range step, number. Defaults to <code>any</code>.', 131 value: 'Lower and upper range bounds, array of number. Defaults to <code>[min, max]</code>.', 132 theme: 'Widget CSS, string. Defaults to [<code>theme_Flat</code>](#doc_theme_Flat).', 133 color: 'Range color as CSS \`color\` value. Defaults to the theme.', 134 width: 'Range width, either number of pixels or a CSS \`width\` string. Defaults to the theme.', 135}).map(([k,v]) => ` - \`${k}:\` ${v}\n`)} 136 137` 138}) 139)} 140 141function _doc_rangeSlider(signature,rangeSlider,md){return( 142signature(rangeSlider, { 143 description: md` 144 145**Deprecated**. Use [\`interval()\`](#doc_interval) instead. 146 147Widget that imitates the appearance and API of [\`slider()\`](https://
147observablehq.com/@jashkenas/inputs#slider). 148 149Arguments: 150- \`options:\` Optional. Available options: 151${Object.entries({ 152 title: 'Title above widget, string. Defaults to `null`.', 153 description: 'Description below widget, string. Defaults to `null`.', 154 submit: 'Display a submit button to apply changes, boolean. Defaults to <code>false</code>.', 155 getValue: 'Value callback, Function. Defaults to <code>n => n.value.map(roundToPrecision)</code>.', 156 color: 'CSS color for range. Defaults to the theme.', 157 separator: 'Value separator. Defaults to <code>" ⦠"</code>.', 158 precision: 'Number of decimals as Number. Defaults to <code>3</code>.', 159 format: 'Display format as [d3-format](https://github.com/d3/d3-format) string or Function. Defaults to <code>v => v</code>.', 160 display: 'Display formatter as Function. Defaults to <code>v => v.map(format).join(separator)</code>', 161 }).map(([k,v]) => ` - \`${k}:\` ${v}\n`)}` 162 163}) 164)} 165 166function _section_themes(md){return( 167md`--- 168## Themes 169` 170)} 171 172function _example_theme_options(Inputs){return( 173Inputs.form({ 174 color: Inputs.color({label: 'Range color', value: '#3b99fc'}), 175}) 176)} 177 178function _doc_theme_Flat(signature,md,SLUG,_themeDemoInput,theme_Flat,example_theme_options,invalidation){return( 179signature('theme_Flat', { 180 name: 'theme_Flat', 181 description: md` 182Default theme. An unshaded version of [\`theme_GoogleChrome_MacOS1013\`](#doc_theme_GoogleChrome_MacOS1013). 183 184~~~js 185import {theme_Flat} from '${SLUG}' 186~~~ 187 188${_themeDemoInput(theme_Flat, example_theme_options, invalidation)} 189 ` 190}) 191)} 192 193function _doc_theme_GoogleChrome_MacOS1013(signature,md,SLUG,_themeDemoInput,theme_GoogleChrome_MacOS1013,example_theme_options,invalidation){return( 194signature('theme_GoogleChrome_MacOS1013', { 195 name: 'theme_GoogleChrome_MacOS1013', 196 description: md` 197Matches the style of range inputs in Google Chrome on macOS 10.13. 198 199~~~js 200import {theme_GoogleChrome_MacOS1013} from '${SLUG}' 201~~~ 202 203${_themeDemoInput(theme_GoogleChrome_MacOS1013, example_theme_options, invalidation)} 204` 205}) 206)} 207 208function _doc_theme_Retro1(signature,md,SLUG,_themeDemoInput,theme_Retro1,example_theme_options,invalidation){return( 209signature('theme_Retro1', { 210 name: 'theme_Retro1', 211 description: md` 212Minimal theme that showcases the bare requirements. 213 214~~~js 215import {theme_Retro1} from '${SLUG}' 216~~~ 217 218${_themeDemoInput(theme_Retro1, example_theme_options, invalidation)} 219` 220}) 221)} 222 223function _doc_theme_NoUiSlider(signature,md,SLUG,_themeDemoInput,theme_NoUiSlider,example_theme_options,invalidation){return( 224signature('theme_NoUiSlider', { 225 name: 'theme_NoUiSlider', 226 description: md` 227Replicates the style of the [noUiSlider library](https://refreshless.com/nouislider/examples/). 228 229~~~js 230import {theme_NoUiSlider} from '${SLUG}' 231~~~ 232 233${_themeDemoInput(theme_NoUiSlider, example_theme_options, invalidation)} 234` 235}) 236)} 237 238function __themeDemoInput(rangeInput,Inputs) 239{ 240 const masterInput = rangeInput({value: [25, 75], theme: ''}); 241 return (theme, {widget, ...options}, invalidation) => Inputs.bind( 242 rangeInput({theme, ...options}), 243 masterInput, 244 invalidation 245 ); 246} 247 248 249function _20(md){return( 250md`--- 251## Implementation` 252)} 253 254function _interval(DOM,rangeInput,html){return( 255function interval(range = [], options = {}) { 256 const [min = 0, max = 1] = range; 257 const { 258 step = .001, 259 label = null, 260 value = [min, max], 261 format = ([start, end]) => `${start} ⦠${end}`, 262 color, 263 width, 264 theme, 265 } = options; 266 267 const __ns__ = DOM.uid('scope').id; 268 const css = ` 269#${__ns__} { 270 font: 13px/1.2 var(--sans-serif); 271 display: flex; 272 align-items: baseline; 273 flex-wrap: wrap; 274 max-width: 100%; 275 width: auto; 276} 277@media only screen and (min-width: 30em) { 278 #${__ns__} { 279 flex-wrap: nowrap; 280 width: 360px; 281 } 282} 283#${__ns__} .label { 284 width: 120px; 285 padding: 5px 0 4px 0; 286 margin-right: 6.5px; 287 flex-shrink: 0; 288} 289#${__ns__} .form { 290 display: flex; 291 width: 100%; 292} 293#${__ns__} .range { 294 flex-shrink: 1; 295 width: 100%; 296} 297#${__ns__} .range-slider { 298 width: 100%; 299} 300 `; 301 302 const $range = rangeInput({min, max, value: [value[0], value[1]], step, color, width, theme}); 303 const $output = html`<output>`; 304 const $view = html`<div id=${__ns__}>
304 305${label == null ? '' : html`<div class="label">${label}`} 306<div class=form> 307 <div class=range> 308 ${$range}<div class=range-output>${$output}</div> 309 </div> 310</div> 311${html`<style>${css}`} 312 `; 313 314 const update = () => { 315 const content = format([$range.value[0], $range.value[1]]); 316 if(typeof content === 'string') $output.value = content; 317 else { 318 while($output.lastChild) $output.lastChild.remove(); 319 $output.appendChild(content); 320 } 321 }; 322 $range.oninput = update; 323 update(); 324 325 return Object.defineProperty($view, 'value', { 326 get: () => $range.value, 327 set: ([a, b]) => { 328 $range.value = [a, b]; 329 update(); 330 }, 331 }); 332} 333)} 334 335function _rangeInput(theme_Flat,randomScope,htl,cssLength,Event,invalidation){return( 336function rangeInput(options = {}) { 337 const { 338 min = 0, 339 max = 100, 340 step = 'any', 341 value: defaultValue = [min, max], 342 color, 343 width, 344 theme = theme_Flat, 345 } = options; 346 347 const controls = {}; 348 const scope = randomScope(); 349 const clamp = (a, b, v) => v < a ? a : v > b ? b : v; 350 const html = htl.html; 351 352 // Will be used to sanitize values while avoiding floating point issues. 353 const input = html`<input type=range ${{min, max, step}}>`; 354 355 const dom = html`<div class=${`${scope} range-slider`} style=${{ 356 color,
357 width: cssLength(width), 358 }}> 359 ${controls.track = html`<div class="range-track"> 360 ${controls.zone = html`<div class="range-track-zone"> 361 ${controls.range = html`<div class="range-select" tabindex=0> 362 ${controls.min = html`<div class="thumb thumb-min" tabindex=0>`} 363 ${controls.max = html`<div class="thumb thumb-max" tabindex=0>`} 364 `} 365 `} 366 `} 367 ${html`<style>${theme.replace(/:scope\b/g, '.'+scope)}`} 368</div>`; 369 370 let value = [], changed = false; 371 Object.defineProperty(dom, 'value', { 372 get: () => [...value], 373 set: ([a, b]) => { 374 value = sanitize(a, b); 375 updateRange(); 376 }, 377 }); 378 379 const sanitize = (a, b) => { 380 a = isNaN(a) ? min : ((input.value = a), input.valueAsNumber); 381 b = isNaN(b) ? max : ((input.value = b), input.valueAsNumber); 382 return [Math.min(a, b), Math.max(a, b)]; 383 } 384 385 const updateRange = () => { 386 const ratio = v => (v - min) / (max - min); 387 dom.style.setProperty('--range-min', `${ratio(value[0]) * 100}%`); 388 dom.style.setProperty('--range-max', `${ratio(value[1]) * 100}%`); 389 }; 390 391 const dispatch = name => { 392 dom.dispatchEvent(new Event(name, {bubbles: true})); 393 }; 394 const setValue = (vmin, vmax) => { 395 const [pmin, pmax] = value; 396 value = sanitize(vmin, vmax); 397 updateRange();
398 // Only dispatch if values have changed. 399 if(pmin === value[0] && pmax === value[1]) return; 400 dispatch('input'); 401 changed = true; 402 }; 403 404 setValue(...defaultValue); 405 406 // Mousemove handlers. 407 const handlers = new Map([ 408 [controls.min, (dt, ov) => { 409 const v = clamp(min, ov[1], ov[0] + dt * (max - min)); 410 setValue(v, ov[1]); 411 }], 412 [controls.max, (dt, ov) => { 413 const v = clamp(ov[0], max, ov[1] + dt * (max - min)); 414 setValue(ov[0], v); 415 }], 416 [controls.range, (dt, ov) => { 417 const d = ov[1] - ov[0]; 418 const v = clamp(min, max - d, ov[0] + dt * (max - min)); 419 setValue(v, v + d); 420 }], 421 ]); 422 423 // Returns client offset object. 424 const pointer = e => e.touches ? e.touches[0] : e; 425 // Note: Chrome defaults "passive" for touch events to true. 426 const on = (e, fn) => e.split(' ').map(e => document.addEventListener(e, fn, {passive: false})); 427 const off = (e, fn) => e.split(' ').map(e => document.removeEventListener(e, fn, {passive: false})); 428 429 let initialX, initialV, target, dragging = false; 430 function handleDrag(e) { 431 // Gracefully handle exit and reentry of the viewport. 432 if(!e.buttons && !e.touches) { 433 handleDragStop(); 434 return; 435 } 436 dragging = true; 437 const w = controls.zone.getBoundingClientRect().width; 438 e.preventDefault(); 439 handlers.get(target)((pointer(e).clientX - initialX) / w, initialV); 440 } 441 442 443 function handleDragStop(e) { 444 off('mousemove touchmove', handleDrag); 445 off('mouseup touchend', handleDragStop); 446 if(changed) dispatch('change'); 447 } 448 449 invalidation.then(handleDragStop); 450 451 dom.ontouchstart = dom.onmousedown = e => { 452 dragging = false; 453 changed = false; 454 if(!handlers.has(e.target)) return; 455 on('mousemove touchmove', handleDrag); 456 on('mouseup touchend', handleDragStop); 457 e.preventDefault(); 458 e.stopPropagation(); 459 460 target = e.target; 461 initialX = pointer(e).clientX; 462 initialV = value.slice(); 463 }; 464 465 controls.track.onclick = e => { 466 if(dragging) return; 467 changed = false; 468 const r = controls.zone.getBoundingClientRect(); 469 const t = clamp(0, 1, (pointer(e).clientX - r.left) / r.width); 470 const v = min + t * (max - min); 471 const [vmin, vmax] = value, d = vmax - vmin; 472 if(v < vmin) setValue(v, v + d); 473 else if(v > vmax) setValue(v - d, v); 474 if(changed) dispatch('change'); 475 }; 476 477 return dom; 478} 479)} 480 481function _23(md){return( 482md`### Themes` 483)} 484 485function _theme_Flat(){return( 486` 487/* Options */ 488:scope { 489 color: #3b99fc; 490 width: 240px; 491} 492 493:scope { 494 position: relative; 495 display: inline-block; 496 --thumb-size: 15px; 497 --thumb-radius: calc(var(--thumb-size) / 2); 498 padding: var(--thumb-radius) 0; 499 margin: 2px; 500 vertical-align: middle; 501} 502:scope .range-track { 503 box-sizing: border-box; 504 position: relative; 505 height: 7px; 506 background-color: hsl(0, 0%, 80%); 507 overflow: visible; 508 border-radius: 4px; 509 padding: 0 var(--thumb-radius); 510} 511:scope .range-track-zone { 512 box-sizing: border-box; 513 position: relative; 514} 515:scope .range-select { 516 box-sizing: border-box; 517 position: relative; 518 left: var(--range-min); 519 width: calc(var(--range-max) - var(--range-min)); 520 cursor: ew-resize; 521 background: currentColor; 522 height: 7px; 523 border: inherit; 524} 525/* Expands the hotspot area. */ 526:scope .range-select:before { 527 content: ""; 528 position: absolute; 529 width: 100%; 530 height: var(--thumb-size); 531 left: 0; 532 top: calc(2px - var(--thumb-radius)); 533} 534:scope .range-select:focus, 535:scope .thumb:focus { 536 outline: none; 537} 538:scope .thumb { 539 box-sizing: border-box; 540 position: absolute; 541 width: var(--thumb-size); 542 height: var(--thumb-size); 543 544 background: #fcfcfc; 545 top: -4px; 546 border-radius: 100%; 547 border: 1px solid hsl(0,0%,55%); 548 cursor: default; 549 margin: 0; 550} 551:scope .thumb:active { 552 box-shadow: inset 0 var(--thumb-size) #0002; 553} 554:scope .thumb-min { 555 left: calc(-1px - var(--thumb-radius)); 556} 557:scope .thumb-max { 558 right: calc(-1px - var(--thumb-radius)); 559} 560` 561)} 562 563function _theme_GoogleChrome_MacOS1013(){return( 564`
565/* Options */ 566:scope { 567 color: #3b99fc; 568 width: 240px; 569} 570 571:scope { 572 position: relative; 573 display: inline-block; 574 --thumb-size: 15px; 575 --thumb-radius: calc(var(--thumb-size) / 2); 576 padding: var(--thumb-radius) 0; 577 margin: 2px; 578 vertical-align: middle; 579} 580:scope .range-track { 581 box-sizing: border-box; 582 position: relative; 583 height: 5px; 584 background-color: hsl(0, 0%, 80%); 585 box-shadow: inset 0 1px 3px -1px rgba(0,0,0,0.33); 586 overflow: visible; 587 border-radius: 3px; 588 border: 1px inset hsl(0, 0%, 70%); 589 padding: 0 var(--thumb-radius); 590} 591:scope .range-track-zone { 592 box-sizing: border-box; 593 position: relative; 594} 595:scope .range-select { 596 box-sizing: border-box; 597 position: relative; 598 left: var(--range-min); 599 width: calc(var(--range-max) - var(--range-min)); 600 cursor: ew-resize; 601 background: currentColor; 602 height: 5px; 603 top: -1px; 604 border: inherit; 605} 606/* Expands the hotspot area. */ 607:scope .range-select:before { 608 content: ""; 609 position: absolute; 610 width: 100%; 611 height: var(--thumb-size); 612 left: 0; 613 top: calc(2px - var(--thumb-radius)); 614} 615:scope .range-select:focus, 616:scope .thumb:focus { 617 outline: none; 618} 619:scope .thumb { 620 box-sizing: border-box; 621 position: absolute; 622 width: var(--thumb-size); 623 height: var(--thumb-size); 624 625 background: #eee linear-gradient(0deg, #fff0 50%, #fff9 50%, #fff5); 626 top: -5px; 627 border-radius: 100%; 628 border: 1px solid hsl(0,0%,55%); 629 cursor: default; 630 margin: 0; 631} 632:scope .thumb:active { 633 box-shadow: inset 0 var(--thumb-size) #0002; 634} 635:scope .thumb-min { 636 left: calc(-1px - var(--thumb-radius)); 637} 638:scope .thumb-max { 639 right: calc(-1px - var(--thumb-radius)); 640} 641` 642)} 643 644function _theme_Retro1(){return( 645` 646/* Options */ 647:scope { 648 color: #3b99fc; 649 width: 240px; 650} 651 652:scope { 653 position: relative; 654 display: inline-block; 655 vertical-align: -10px; 656 margin: 2px; 657} 658:scope .range-track { 659 height: 20px; 660 border: 2px solid #000; 661 padding: 0 18px; 662 position: relative; 663 background: #fff url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAPUlEQVQoU2NkYGD4z8DAwMiAH/wnpACunWyFIGeAAIYB6ALYFILFiLGaaIXY3YIrlLBZjdVDIIXoAY7VQwD4rQoH9uQ3nwAAAABJRU5ErkJggg=="); 664} 665:scope .range-track-zone { 666 position: relative; 667 height: 100%; 668} 669:scope .range-select { 670 box-sizing: border-box; 671 position: relative; 672 left: var(--range-min); 673 width: calc(var(--range-max) - var(--range-min)); 674 height: 100%; 675 cursor: ew-resize; 676 background: currentColor url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAK0lEQVQoU2NkwA/+g6QZqaEIbAY2k8BWIMuRrQjDmTCTMKxAVkmSIrwhAQBStQYIBYnwugAAAABJRU5ErkJggg==") fixed; 677} 678:scope .range-select:focus, 679:scope .thumb:focus { 680 outline: none; 681} 682:scope .thumb { 683 box-sizing: border-box; 684 position: absolute; 685 height: 100%; 686 top: 0; 687 width: 20px; 688 background: #fff; 689 border: 2px solid #000; 690 border-width: 0 2px; 691 cursor: default; 692} 693:scope .thumb:active { 694 background: #000; 695} 696:scope .thumb-min { 697 left: -20px; 698} 699:scope .thumb-max { 700 right: -20px; 701} 702` 703)} 704 705function _theme_NoUiSlider(){return( 706` 707/* Options */ 708:scope { 709 color: #3b99fc; 710 width: 240px; 711} 712 713:scope { 714 box-sizing: border-box; 715 display: inline-block; 716 vertical-align: middle; 717} 718:scope .range-track { 719 box-sizing: border-box; 720 margin: 10px 17px; 721 position: relative; 722 background: #FAFAFA; 723 border-radius: 4px; 724 border: 1px solid #D3D3D3; 725 box-shadow: inset 0 1px 1px #F0F0F0, 0 3px 6px -5px #BBB; 726 height: 18px; 727} 728:scope .range-select { 729 box-sizing: border-box; 730 position: absolute; 731 background: currentColor; 732 left: var(--range-min); 733 width: calc(var(--range-max) - var(--range-min)); 734 height: 100%; 735 cursor: ew-resize; 736} 737:scope .thumb { 738 box-sizing: border-box; 739 position: absolute; 740 width: 34px; 741 height: 28px; 742 top: -6px; 743 border: 1px solid #D9D9D9; 744 border-radius: 3px; 745 background: #FFF; 746 cursor: default; 747 box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #EBEBEB, 0 3px 6px -3px #BBB; 748} 749:scope .thumb:before, 750:scope .thumb:after { 751 content: ""; 752 display: block; 753 position: absolute; 754 height: 14px; 755 width: 1px; 756 background: #E8E7E6; 757 left: 14px; 758 top: 6px; 759} 760:scope .thumb:after { 761 left: 17px; 762} 763:scope .thumb-min { 764 left: -17px; 765} 766:scope .thumb-max { 767 right: -17px; 768} 769` 770)} 771 772function _28(md){return( 773md`### Utilities` 774)} 775 776function _randomScope(){return( 777function randomScope(prefix = 'scope-') { 778 return prefix + (performance.now() + Math.random()).toString(32).replace('.', '-'); 779} 780)} 781 782function _html(htl){return( 783htl.html 784)} 785 786function _cssLength(){return( 787v => v == null ? null : typeof v === 'number' ? `${v}px` : `${v}` 788)} 789 790function _32(md){return( 791md`### Documentation` 792)} 793 794function _requireCompat(invalidation){return(
795function requireCompat({invalidation: invalidated = invalidation} = {}) { 796 const DEFINE = 'define'; 797 const desc = Object.getOwnPropertyDescriptor(window, DEFINE); 798 if(desc && (!('value' in desc) || !desc.configurable || !desc.writable || !desc.enumerable)) { 799 throw Error('Cannot override non-default descriptor'); 800 } 801 802 const defines = new Set(window.define ? [window.define] : []); 803 804 const bridge = function define(...args) { 805 for(const define of defines) { 806 try { define.apply(window, args) } catch(e) {} 807 } 808 }; 809 bridge.amd = {}; 810 811 Object.defineProperty(window, DEFINE, { 812 get: () => bridge, 813 set: value => { defines.add(value) }, 814 enumerable: true, 815 configurable: true, 816 }); 817 818 const disable = () => { 819 Object.defineProperty(window, DEFINE, desc || { 820 value: desc?.value, 821 enumerable: true, 822 writable: true, 823 configurable: true, 824 }); 825 }; 826 invalidated.then(disable); 827 return disable; 828} 829)} 830 831function _disableCompat(requireCompat,invalidation){return( 832requireCompat({invalidation}) 833)} 834 835function _SLUG(){return( 836'@mootari/range-slider' 837)} 838 839function _37(md){return( 840md`### Deprecated` 841)} 842 843function _rangeSlider(rangeInput,d3format,jashkenasInput,html){return( 844function rangeSlider(options = {}) { 845 const {min = 0, max = 1, step = 'any', value, color, theme} = options; 846 const input = rangeInput({min, max, step, value, theme}); 847 if(color) input.style.color = color; 848 849 const {precision = 3, format = v => v, display: _display, separator = ' ⦠'} = options; 850 const round = (p => (p = 10 ** p, v => Math.round(v * p) / p))(precision); 851 const output = typeof format === 'function' ? format : d3format.format(format); 852 const display = _display || (v => v.map(output).join(separator)); 853 854 const { 855 title, description, submit, 856 getValue = n => n.value.map(round), 857 } = options; 858 859 const w = jashkenasInput({ 860 title, description, submit, display, getValue, 861 form: Object.assign(html`<form>${input}`, {input}), 862 }); 863 864 w.querySelector('output').style.display = 'inline-block'; 865 return w; 866} 867)} 868 869function _themes(theme_Flat,theme_GoogleChrome_MacOS1013,theme_NoUiSlider,theme_Retro1){return( 870{ 871 'Flat': theme_Flat, 872 'Chrome macOS': theme_GoogleChrome_MacOS1013, 873 'noUiSlider': theme_NoUiSlider, 874 'Retro': theme_Retro1, 875} 876)} 877 878function _jashkenasInput(html,d3format){return( 879function input(config) { 880 let { 881 form, 882 type = "text", 883 attributes = {}, 884 action, 885 getValue, 886 title, 887 description, 888 format, 889 display, 890 submit, 891 options 892 } = config; 893 const wrapper = html`<div></div>`; 894 if (!form) 895 form = html`<form> 896 <input name=input type=${type} /> 897 </form>`; 898 Object.keys(attributes).forEach(key => { 899 const val = attributes[key]; 900 if (val != null) form.input.setAttribute(key, val); 901 }); 902 if (submit) 903 form.append( 904 html`<input name=submit type=submit style="margin: 0 0.75em" value="${ 905 typeof submit == "string" ? submit : "Submit" 906 }" />` 907 ); 908 form.append( 909 html`<output name=output style="font: 14px Menlo, Consolas, monospace; margin-left: 0.5em;"></output>` 910 ); 911 if (title) 912 form.prepend( 913 html`<div style="font: 700 0.9rem sans-serif; margin-bottom: 3px;">${title}</div>` 914 ); 915 if (description) 916 form.append( 917 html`<div style="font-size: 0.85rem; font-style: italic; margin-top: 3px;">${description}</div>` 918 ); 919 if (format) 920 format = typeof format === "function" ? format : d3format.format(format); 921 if (action) { 922 action(form); 923 } else { 924 const verb = submit 925 ? "onsubmit" 926 : type == "button" 927 ? "onclick" 928 : type == "checkbox" || type == "radio" 929 ? "onchange" 930 : "oninput"; 931 form[verb] = e => { 932 e && e.preventDefault(); 933 const value = getValue ? getValue(form.input) : form.input.value; 934 if (form.output) { 935 const out = display ? display(value) : format ? format(value) : value; 936 if (out instanceof window.Element) { 937 while (form.output.hasChildNodes()) { 938 form.output.removeChild(form.output.lastChild); 939 } 940 form.output.append(out); 941 } else { 942 form.output.value = out; 943 } 944 } 945 form.value = value; 946 if (verb !== "oninput") 947 form.dispatchEvent(new CustomEvent("input", { bubbles: true })); 948 }; 949 if (verb !== "oninput") 950 wrapper.oninput = e => e && e.stopPropagation() && e.preventDefault(); 951 if (verb !== "onsubmit") form.onsubmit = e => e && e.preventDefault(); 952 form[verb](); 953 } 954 while (form.childNodes.length) { 955 wrapper.appendChild(form.childNodes[0]); 956 } 957 form.append(wrapper); 958 return form; 959} 960)} 961 962function _d3format(require){return( 963require("d3-format@1") 964)} 965 966function _doc_changelog(md) 967{
968 const rev = (d, n = '') => `${d} ([${n || 'latest'}](https://observablehq.com/@mootari/range-slider${n}))`; 969 return md` 970--- 971## Changelog 972- ${rev('2023-05-03')} 973 - Added ISC license 974- ${rev('2023-04-30', '@1800')} 975 - Removed \`lazyImport()\` in favor of the v4 module format. 976- ${rev('2022-05-22', '@1786')} 977 - Changed the \`step\` default value in \`rangeInput\` to \`"any"\`. 978 - \`rangeInput\` accepts new options \`color\` and \`width\`. 979 - \`interval\` accepts new options \`format\`, \`theme\`, \`color\` and \`width\`. 980 - \`interval\` uses a single \`<output>\` element. 981 - Removed \`clamp\` cell. 982 - Inlined \`d3format\` and \`input\` from \`@jashkenas/inputs\`, removed lazy dependency. 983 - Deprecated \`rangeSlider\` and \`themes\`. 984 - Restructured documentation. 985- ${rev('2022-05-18', '@1342')} 986 - Fixed \`"Invalid module"\` errors on slow networks. 987- ${rev('2022-04-03', '@1327')} 988 - Dispatch \`Event\` instead of \`CustomEvent\`. 989 - Dispatch \`change\` events. 990- ${rev('2022-01-13', '@1312')}: 991 - Added setter to \`rangeInput()\` and \`interval()\`. 992 - Fixed \`interval()\` events not bubbling. 993- ${rev('2021-12-31', '@1208')}: 994 - Added provisional \`interval()\` widget to match the style of Observable's \`Inputs\` library. Thanks to [Sylvain Lesage](https://observablehq.com/@severo) for suggesting the name! 995 - Removed a few static imports. 996- ${rev('2020-03-27', '@1004')}: Added new default theme "Flat", a more neutral version of the macOS Chrome theme. (via [Jed Fox](https://observablehq.com/@j-f1)) 997- ${rev('2020-03-27', '@930')}: Changelog start. 998`; 999} 1000 1001 1002function _43(md){return( 1003md`--- 1004## Remaining Tasks / Work in progress 1005 1006*Code in this section is subject to change. Only import with a pinned version!* 1007 1008- Delta constraints (min/max range) 1009- Keyboard controls 1010- Compare with control guidelines in https://material.io/components/sliders 1011` 1012)} 1013 1014export default function define(runtime, observer) { 1015 const main = runtime.module(); 1016 main.variable(observer()).define(["md"], _1); 1017 main.variable(observer("section_examples")).define("section_examples", ["md"], _section_examples); 1018 main.variable(observer("example_observable")).define("example_observable", ["SLUG","md"], _example_observable); 1019 main.variable(observer()).define(["interval"], _4); 1020 main.variable(observer("example_jashkenas")).define("example_jashkenas", ["SLUG","md"], _example_jashkenas); 1021 main.variable(observer()).define(["rangeSlider"], _6); 1022 main.variable(observer("example_basic")).define("example_basic", ["SLUG","md"], _example_basic); 1023 main.variable(observer()).define(["rangeInput"], _8); 1024 main.variable(observer("section_docs")).define("section_docs", ["md"], _section_docs); 1025 main.variable(observer("doc_interval")).define("doc_interval", ["signature","interval","md"], _doc_interval); 1026 main.variable(observer("doc_rangeInput")).define("doc_rangeInput", ["signature","rangeInput","md"], _doc_rangeInput); 1027 main.variable(observer("doc_rangeSlider")).define("doc_rangeSlider", ["signature","rangeSlider","md"], _doc_rangeSlider); 1028 main.variable(observer("section_themes")).define("section_themes", ["md"], _section_themes); 1029 main.variable(observer("viewof example_theme_options")).define("viewof example_theme_options", ["Inputs"], _example_theme_options); 1030 main.variable(observer("example_theme_options")).define("example_theme_options", ["Generators", "viewof example_theme_options"], (G, _) => G.input(_)); 1031 main.variable(observer("doc_theme_Flat")).define("doc_theme_Flat", ["signature","md","SLUG","_themeDemoInput","theme_Flat","example_theme_options","invalidation"], _doc_theme_Flat); 1032 main.variable(observer("doc_theme_GoogleChrome_MacOS1013")).define("doc_theme_GoogleChrome_MacOS1013", ["signature","md","SLUG","_themeDemoInput","theme_GoogleChrome_MacOS1013","example_theme_options","invalidation"], _doc_theme_GoogleChrome_MacOS1013); 1033 main.variable(observer("doc_theme_Retro1")).define("doc_theme_Retro1", ["signature","md","SLUG","_themeDemoInput","theme_Retro1","example_theme_options","invali
1033dation"], _doc_theme_Retro1); 1034 main.variable(observer("doc_theme_NoUiSlider")).define("doc_theme_NoUiSlider", ["signature","md","SLUG","_themeDemoInput","theme_NoUiSlider","example_theme_options","invalidation"], _doc_theme_NoUiSlider); 1035 main.variable(observer("_themeDemoInput")).define("_themeDemoInput", ["rangeInput","Inputs"], __themeDemoInput); 1036 main.variable(observer()).define(["md"], _20); 1037 main.variable(observer("interval")).define("interval", ["DOM","rangeInput","html"], _interval); 1038 main.variable(observer("rangeInput")).define("rangeInput", ["theme_Flat","randomScope","htl","cssLength","Event","invalidation"], _rangeInput); 1039 main.variable(observer()).define(["md"], _23); 1040 main.variable(observer("theme_Flat")).define("theme_Flat", _theme_Flat); 1041 main.variable(observer("theme_GoogleChrome_MacOS1013")).define("theme_GoogleChrome_MacOS1013", _theme_GoogleChrome_MacOS1013); 1042 main.variable(observer("theme_Retro1")).define("theme_Retro1", _theme_Retro1); 1043 main.variable(observer("theme_NoUiSlider")).define("theme_NoUiSlider", _theme_NoUiSlider); 1044 main.variable(observer()).define(["md"], _28); 1045 main.variable(observer("randomScope")).define("randomScope", _randomScope); 1046 main.variable(observer("html")).define("html", ["htl"], _html); 1047 main.variable(observer("cssLength")).define("cssLength", _cssLength); 1048 main.variable(observer()).define(["md"], _32); 1049 const child1 = runtime.module(define1); 1050 main.import("signature", child1); 1051 main.variable(observer("requireCompat")).define("requireCompat", ["invalidation"], _requireCompat); 1052 main.variable(observer("disableCompat")).define("disableCompat", ["requireCompat","invalidation"], _disableCompat); 1053 main.variable(observer("SLUG")).define("SLUG", _SLUG); 1054 main.variable(observer()).define(["md"], _37); 1055 main.variable(observer("rangeSlider")).define("rangeSlider", ["rangeInput","d3format","jashkenasInput","html"], _rangeSlider); 1056 main.variable(observer("themes")).define("themes", ["theme_Flat","theme_GoogleChrome_MacOS1013","theme_NoUiSlider","theme_Retro1"], _themes); 1057 main.variable(observer("jashkenasInput")).define("jashkenasInput", ["html","d3format"], _jashkenasInput); 1058 main.variable(observer("d3format")).define("d3format", ["require"], _d3format); 1059 main.variable(observer("doc_changelog")).define("doc_changelog", ["md"], _doc_changelog); 1060 main.variable(observer()).define(["md"], _43); 1061 return main; 1062}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.