|
13 | 13 | import { sizeToCSS } from '$lib/helper.js';
|
14 | 14 | import { onMount, createEventDispatcher } from 'svelte';
|
15 | 15 | import Debouncer from '$lib/debouncer.js';
|
| 16 | + import RangeInput from './RangeInput.svelte'; |
16 | 17 |
|
17 | 18 | const dispatch = createEventDispatcher();
|
18 | 19 | const debouncer = new Debouncer();
|
|
22 | 23 | export let max = NaN;
|
23 | 24 | export let step = 1;
|
24 | 25 |
|
25 |
| - export let inputElement: HTMLInputElement | null = null; |
26 |
| - let ID: string; |
27 |
| -
|
28 |
| - $: value, |
29 |
| - (() => { |
30 |
| - if (!inputElement) return; |
31 |
| -
|
32 |
| - const oldValue = parseFloat(inputElement.value); |
33 |
| - if (value == oldValue) return; // Avoid unnecessary updates. |
34 |
| -
|
35 |
| - // @ts-ignore |
36 |
| - inputElement.value = value; |
37 |
| - })(); |
38 |
| -
|
39 |
| - onMount(() => { |
40 |
| - if (!inputElement) return; // Shut up typescript. |
41 |
| -
|
42 |
| - // @ts-ignore |
43 |
| - inputElement.value = value; |
44 |
| -
|
45 |
| - inputElement.addEventListener('input', () => { |
46 |
| - if (!inputElement) return; |
47 |
| -
|
48 |
| - const newValue = parseFloat(inputElement.value); |
49 |
| - if (newValue == value) return; // Avoid unnecessary updates. |
50 |
| -
|
51 |
| - value = newValue; |
52 |
| - dispatch('update', newValue); |
53 |
| -
|
54 |
| - debouncer.debounce(() => { |
55 |
| - dispatch('update-debounced', value); |
56 |
| - }); |
57 |
| - }); |
58 |
| -
|
59 |
| - inputElement.addEventListener('change', () => { |
60 |
| - dispatch('update-unfocused', value); |
61 |
| - }); |
62 |
| -
|
63 |
| - inputElement.addEventListener( |
64 |
| - 'wheel', |
65 |
| - (event) => { |
66 |
| - if (!inputElement) return; |
67 |
| - if (document.activeElement != inputElement) return; // We don't have focus. |
68 |
| - if (inputElement.disabled) return; // Don't. |
69 |
| -
|
70 |
| - if (event.deltaY != 0 && event.deltaY != -0) { |
71 |
| - if (event.deltaY > 1) { |
72 |
| - value -= step; |
73 |
| - } else { |
74 |
| - value += step; |
75 |
| - } |
76 |
| - } |
77 |
| - if (event.deltaX != 0 && event.deltaX != -0) { |
78 |
| - if (event.deltaX > 1) { |
79 |
| - value += step; |
80 |
| - } else { |
81 |
| - value -= step; |
82 |
| - } |
83 |
| - } |
84 |
| - }, |
85 |
| - { passive: true } |
86 |
| - ); |
87 |
| - }); |
| 26 | + export let ID: string = ''; |
88 | 27 | </script>
|
89 | 28 |
|
90 | 29 | <!-- I hate having to repeat myself like this. Oh well. -->
|
|
95 | 34 | </label>
|
96 | 35 |
|
97 | 36 | <div class="clui-input-range-container" style:width={sizeToCSS($$restProps.width)}>
|
98 |
| - <div class="clui-input-range-sister" aria-hidden="true" style:padding={sizeToCSS($$restProps.padding || 0.5)}> |
99 |
| - <NumberInput bind:value {...$$restProps} width="full" borderless={true} padding={0} /> |
100 |
| - <div class="clui-input-range-sister-dummy"> |
| 37 | + <div class="clui-input-range-sister" style:padding={sizeToCSS($$restProps.padding || 0.5)}> |
| 38 | + <!-- Note that this is the REAL input element for screen readers! --> |
| 39 | + <NumberInput |
| 40 | + bind:ID |
| 41 | + bind:value |
| 42 | + {...$$restProps} |
| 43 | + width="full" |
| 44 | + borderless={true} |
| 45 | + padding={0} |
| 46 | + on:update={(v) => dispatch('update', v)} |
| 47 | + on:update-debounced={(v) => dispatch('update-debounced', v)} |
| 48 | + on:update-unfocused={(v) => dispatch('update-unfocused', v)} |
| 49 | + /> |
| 50 | + |
| 51 | + <div class="clui-input-range-sister-dummy" aria-hidden="true"> |
101 | 52 | <!-- We use this as a calculation for the width of this part. -->
|
102 | 53 | {value}
|
103 | 54 | </div>
|
104 | 55 | </div>
|
105 | 56 |
|
106 | 57 | {#if $$slots.unit}
|
107 |
| - <div style="margin-right: 2px;" style:padding-top={sizeToCSS($$restProps.padding || 0.5)}> |
108 |
| - <slot name="unit" /> |
| 58 | + <div class="clui-input-range-unit" style="margin-right: 2px;"> |
| 59 | + <span style="font-size: .85em;"> |
| 60 | + <slot name="unit" /> |
| 61 | + </span> |
109 | 62 | </div>
|
110 | 63 | {/if}
|
111 | 64 |
|
112 |
| - <InternalInput |
113 |
| - bind:ID |
114 |
| - bind:inputElement |
115 |
| - type="range" |
116 |
| - borderless={true} |
117 |
| - properties={{ |
118 |
| - min: min, |
119 |
| - max: max, |
120 |
| - step: step, |
121 |
| - inputmode: 'numeric', |
122 |
| - pattern: 'd*' |
123 |
| - }} |
124 |
| - {...$$restProps} |
125 |
| - width="full" |
126 |
| - hasLabel={false} |
127 |
| - /> |
| 65 | + <div aria-hidden="true" style:display="content"> |
| 66 | + <RangeInput |
| 67 | + bind:value |
| 68 | + borderless={true} |
| 69 | + {...$$restProps} |
| 70 | + width="full" |
| 71 | + on:update={(v) => dispatch('update', v)} |
| 72 | + on:update-debounced={(v) => dispatch('update-debounced', v)} |
| 73 | + on:update-unfocused={(v) => dispatch('update-unfocused', v)} |
| 74 | + /> |
| 75 | + </div> |
128 | 76 | </div>
|
129 | 77 | </div>
|
130 | 78 | {:else}
|
131 | 79 | <div class="clui-input-range-container" style:width={sizeToCSS($$restProps.width)}>
|
132 |
| - <div class="clui-input-range-sister" aria-hidden="true" style:padding={sizeToCSS($$restProps.padding || 0.5)}> |
133 |
| - <NumberInput bind:value {...$$restProps} width="full" borderless={true} padding={0} /> |
134 |
| - <div class="clui-input-range-sister-dummy"> |
| 80 | + <div class="clui-input-range-sister" style:padding={sizeToCSS($$restProps.padding || 0.5)}> |
| 81 | + <!-- Note that this is the REAL input element for screen readers! --> |
| 82 | + <NumberInput |
| 83 | + bind:ID |
| 84 | + bind:value |
| 85 | + {...$$restProps} |
| 86 | + width="full" |
| 87 | + borderless={true} |
| 88 | + padding={0} |
| 89 | + on:update={(v) => dispatch('update', v)} |
| 90 | + on:update-debounced={(v) => dispatch('update-debounced', v)} |
| 91 | + on:update-unfocused={(v) => dispatch('update-unfocused', v)} |
| 92 | + /> |
| 93 | + |
| 94 | + <div class="clui-input-range-sister-dummy" aria-hidden="true"> |
135 | 95 | <!-- We use this as a calculation for the width of this part. -->
|
136 | 96 | {value}
|
137 | 97 | </div>
|
138 | 98 | </div>
|
139 | 99 |
|
140 | 100 | {#if $$slots.unit}
|
141 |
| - <div style="margin-right: 2px;" style:padding-top={sizeToCSS($$restProps.padding || 0.5)}> |
142 |
| - <slot name="unit" /> |
| 101 | + <div class="clui-input-range-unit" style="margin-right: 2px;"> |
| 102 | + <span style="font-size: .85em;"> |
| 103 | + <slot name="unit" /> |
| 104 | + </span> |
143 | 105 | </div>
|
144 | 106 | {/if}
|
145 | 107 |
|
146 |
| - <InternalInput |
147 |
| - bind:ID |
148 |
| - bind:inputElement |
149 |
| - type="range" |
150 |
| - borderless={true} |
151 |
| - properties={{ |
152 |
| - min: min, |
153 |
| - max: max, |
154 |
| - step: step, |
155 |
| - inputmode: 'numeric', |
156 |
| - pattern: 'd*' |
157 |
| - }} |
158 |
| - {...$$restProps} |
159 |
| - width="full" |
160 |
| - hasLabel={false} |
161 |
| - /> |
| 108 | + <div aria-hidden="true" style:display="content"> |
| 109 | + <RangeInput |
| 110 | + bind:value |
| 111 | + borderless={true} |
| 112 | + {...$$restProps} |
| 113 | + width="full" |
| 114 | + on:update={(v) => dispatch('update', v)} |
| 115 | + on:update-debounced={(v) => dispatch('update-debounced', v)} |
| 116 | + on:update-unfocused={(v) => dispatch('update-unfocused', v)} |
| 117 | + /> |
| 118 | + </div> |
162 | 119 | </div>
|
163 | 120 | {/if}
|
164 | 121 |
|
|
187 | 144 | border-bottom-style: solid;
|
188 | 145 | border-bottom-color: var(--base-7);
|
189 | 146 | }
|
| 147 | + .clui-input-range-unit { |
| 148 | + padding-top: 0.15em; |
| 149 | + } |
190 | 150 |
|
191 | 151 | .clui-input-range-sister-dummy {
|
192 | 152 | display: block;
|
|
0 commit comments