PageSourceSearch

https://data-science.wifo.ac.at/observable/kt-en/[email protected]

js wifo.ac.at collected 2026-09-24 08:53:11 UTC 32,172 bytes, 1,062 lines download raw bytes

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.