馃悕馃悕馃悕
at dev 184 lines 4.3 kB view raw
1 2$css(` 3.number { 4 display: block; 5 line-height: 1.5rem; 6 border-left: 1px solid var(--main-faded); 7 padding-left: 0.5rem; 8} 9 10.number:has(input:focus) { 11 border-left: 3px solid var(--main-solid); 12 padding-left: calc(0.5rem - 2px); 13} 14 15.number label { 16 color: var(--main-solid); 17 min-height: 1em; 18 line-height: 1em; 19 cursor: text; 20} 21 22.number .copyable-value { 23 width: 0; 24 display: inline-block; 25} 26 27.number input[type=number] { 28 background: var(--main-background); 29 color: var(--main-solid); 30 font-family: var(--main-font); 31 transition: border-color 0.2s ease; 32 border-bottom: 1px solid var(--main-faded); 33 min-height: 1rem; 34 line-height: 1rem; 35 width: 5rem; 36} 37 38.number input[type=number] { 39 -webkit-appearance: textfield; 40 -moz-appearance: textfield; 41 appearance: textfield; 42} 43 44.number input[type=number]::-webkit-inner-spin-button, 45.number input[type=number]::-webkit-outer-spin-button { 46 -webkit-appearnce: none; 47} 48 49.number input[type=number]:focus { 50 border-color: var(--main-transparent); 51} 52 53.number input[type=range] { 54 -webkit-appearance: none; 55 appearance: none; 56 width: 100%; 57 outline: none; 58 cursor: pointer; 59 overflow: visible; 60 margin-top: 0.25rem; 61 border: none; 62 border-radius: 2px; 63} 64 65.number input[type=range]:focus { 66 background: none; 67} 68 69/* -webkit: Chromium, Safari, Opera */ 70.number input[type=range]::-webkit-slider-runnable-track { 71 background: var(--main-faded); 72 height: 2px; 73} 74 75.number input[type=range]:focus::-webkit-slider-runnable-track { 76 background: var(--main-solid); 77} 78 79/* -moz: Firefox */ 80.number input[type=range]::-moz-range-track { 81 background: var(--main-faded); 82 height: 2px; 83} 84 85.number input[type=range]:focus::-moz-range-track { 86 background: var(--main-solid); 87} 88 89.number input[type=range]::-webkit-slider-thumb { 90 -webkit-appearance: none; 91 appearance: none; 92 width: 0.5rem; 93 height: 1rem; 94 border-radius: 2px; 95 background: var(--main-solid); 96 margin-top: calc(1px - 1rem); /* center the thumb on the track */ 97 98} 99 100.number input[type=range]::-moz-range-thumb { 101 width: 0.5rem; 102 height: 1rem; 103 border-radius: 2px; 104 border: none; /* cancel default style */ 105 background: var(--main-solid); 106} 107 108`); 109 110const defaults = { 111 label: "x", 112 min: -1.0, 113 max: 1.0, 114 limitField: false, 115 step: 0.01, 116 value: 0, 117 onUpdate: null 118}; 119 120export async function main(target, spec) { 121 spec = { ...defaults, ...spec }; 122 123 const control = document.createElement("div"); 124 control.className = "control number"; 125 126 // TODO ensure uniqueness more rigorously 127 const name = spec.label.toLowerCase().replace(/\s+/g, "-"); 128 129 const label = document.createElement("label"); 130 label.innerText = spec.label; 131 label.id = `${name}-label`; 132 133 const label_eq = document.createElement("span"); 134 label_eq.innerText = " = "; 135 136 const copyable_value = document.createElement("span"); 137 copyable_value.innerText = `${spec.value};\n`; 138 copyable_value.classList = "copyable-value"; 139 label_eq.appendChild(copyable_value); 140 141 label_eq.setAttribute("aria-hidden", true); 142 143 const slider = document.createElement("input"); 144 slider.type = "range"; 145 slider.setAttribute("aria-labelledby", label.id); 146 slider.min = spec.min; 147 slider.max = spec.max; 148 slider.step = spec.step; 149 slider.value = spec.value; 150 151 const field = document.createElement("input"); 152 field.type = "number"; 153 field.setAttribute("aria-labelledby", label.id); 154 if (spec.limitField) { 155 field.min = spec.min; 156 field.max = spec.max; 157 } 158 field.step = spec.step; 159 field.value = spec.value; 160 161 const set = (value) => { 162 slider.value = value; 163 field.value = value; 164 } 165 166 slider.addEventListener("input", () => { 167 field.value = slider.value; 168 copyable_value.innerText = slider.value; 169 spec.onUpdate?.(slider.value, set); 170 }); 171 172 field.addEventListener("input", () => { 173 slider.value = field.value; 174 copyable_value.innerText = field.value; 175 spec.onUpdate?.(field.value, set); 176 }); 177 178 control.appendChild(label); 179 control.appendChild(label_eq); 180 control.appendChild(field); 181 control.appendChild(slider); 182 target.appendChild(control); 183} 184