mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(kit): render the remaining ranges with UiSlider
UiSlider merges a caller's own input, change and blur handlers with its own, leaves the input alone on a null model, and gains unstyled for a range drawn as the browser's own. The rail card setting, observation timeline, imagery opacity, Street Level date and Bhote Koshi split and progress ranges now render through it. The timeline and the setting ranges put the handle back once their owner has answered, since a drag that ends where the slider last rendered leaves its model unchanged. The radio tuner keeps its own input: its owner moves the handle back to the selected station without changing the slot.
This commit is contained in:
1 parent
b9ecb9b9f5
commit
2e43bd9985
12 files changed
+200
-54
No files matched your search
+34
-19
@@ -1,10 +1,14 @@
|
||||
import { h, withDirectives } from 'vue';
|
||||
import { h, mergeProps, normalizeClass, withDirectives } from 'vue';
|
||||
import { vFieldValue } from '../rail/fieldValue.js';
|
||||
import { vRangeValue } from '../rail/rangeValue.js';
|
||||
|
||||
// Server-rendered markup carries the model as the input's value.
|
||||
const ssrValue = ({ value }) => (value == null ? {} : { value });
|
||||
const LIVE = { ...vRangeValue, getSSRProps: ssrValue };
|
||||
// A null model leaves the input as it is.
|
||||
const live = (el, binding) => {
|
||||
if (binding.value != null) vRangeValue.updated(el, binding);
|
||||
};
|
||||
const LIVE = { mounted: live, updated: live, getSSRProps: ssrValue };
|
||||
const HOLD = { ...vFieldValue, getSSRProps: ssrValue };
|
||||
|
||||
/** Put the model back on a held input that lost focus. */
|
||||
@@ -18,11 +22,14 @@ function takeModel(input, props) {
|
||||
* and disabled so the two kits can merge. Core keeps the native range input
|
||||
* (its keyboard, form and styling) where ui-vue draws a `role="slider"`
|
||||
* track. The input carries Core's `gev-quantitative-slider` class ahead of
|
||||
* the caller's, the caller's attributes come first, as in the markup this
|
||||
* replaced, and the accessible name comes as an `aria-label` attribute so
|
||||
* the attribute pass translates it. The value emits as a number.
|
||||
* the caller's (`unstyled` leaves it off, for a range drawn as the browser's
|
||||
* own), the caller's attributes come first, as in the markup this replaced,
|
||||
* and the accessible name comes as an `aria-label` attribute so the attribute
|
||||
* pass translates it. The value emits as a number, and a caller's own input,
|
||||
* change or blur handler still reaches the input.
|
||||
*
|
||||
* The input takes the model's value only when it differs (`v-range-value`);
|
||||
* The input takes the model's value only when it differs (`v-range-value`),
|
||||
* and a null model leaves it as it is;
|
||||
* with `hold`, never while it is focused (`v-field-value`), so a render
|
||||
* cannot move a handle a person is dragging, and it takes the model back
|
||||
* when it loses focus: a model left unchanged renders nothing. Owners take
|
||||
@@ -33,20 +40,27 @@ function takeModel(input, props) {
|
||||
*/
|
||||
export default function UiSlider(props, { attrs, emit }) {
|
||||
const { class: className, ...rest } = attrs;
|
||||
const classes = normalizeClass([
|
||||
props.unstyled ? null : 'gev-quantitative-slider',
|
||||
className,
|
||||
]);
|
||||
return withDirectives(
|
||||
h('input', {
|
||||
...rest,
|
||||
class: ['gev-quantitative-slider', className],
|
||||
type: 'range',
|
||||
min: props.min,
|
||||
max: props.max,
|
||||
step: props.step,
|
||||
disabled: props.disabled,
|
||||
onInput: (event) => emit('update:modelValue', Number(event.target.value)),
|
||||
onBlur: props.hold
|
||||
? (event) => takeModel(event.target, props)
|
||||
: undefined,
|
||||
}),
|
||||
h(
|
||||
'input',
|
||||
mergeProps(rest, {
|
||||
class: classes || undefined,
|
||||
type: 'range',
|
||||
min: props.min,
|
||||
max: props.max,
|
||||
step: props.step,
|
||||
disabled: props.disabled,
|
||||
onInput: (event) =>
|
||||
emit('update:modelValue', Number(event.target.value)),
|
||||
onBlur: props.hold
|
||||
? (event) => takeModel(event.target, props)
|
||||
: undefined,
|
||||
}),
|
||||
),
|
||||
[[props.hold ? HOLD : LIVE, props.modelValue]],
|
||||
);
|
||||
}
|
||||
@@ -58,6 +72,7 @@ UiSlider.props = {
|
||||
step: { type: [Number, String], default: undefined },
|
||||
disabled: { type: Boolean, default: false },
|
||||
hold: { type: Boolean, default: false },
|
||||
unstyled: { type: Boolean, default: false },
|
||||
};
|
||||
UiSlider.emits = ['update:modelValue'];
|
||||
UiSlider.inheritAttrs = false;
|
||||
@@ -105,3 +105,35 @@ test('an input stays until the owner answers, however late', async () => {
|
||||
wrapper.unmount();
|
||||
}
|
||||
});
|
||||
|
||||
test('a caller’s own input, change and blur handlers still run', async () => {
|
||||
const seen = [];
|
||||
const wrapper = mountSlider(
|
||||
{},
|
||||
{
|
||||
onInput: (event) => seen.push(['input', event.target.value]),
|
||||
onChange: () => seen.push(['change']),
|
||||
onBlur: () => seen.push(['blur']),
|
||||
},
|
||||
);
|
||||
const input = wrapper.get('input');
|
||||
input.element.value = '55';
|
||||
await input.trigger('input');
|
||||
await input.trigger('change');
|
||||
await input.trigger('blur');
|
||||
assert.deepEqual(seen, [['input', '55'], ['change'], ['blur']]);
|
||||
assert.deepEqual(wrapper.emitted('update:modelValue'), [[55]]);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('a null model leaves the input, and unstyled leaves Core’s class off', async () => {
|
||||
const wrapper = mountSlider({ modelValue: null, unstyled: true });
|
||||
const input = wrapper.get('input');
|
||||
assert.equal(input.attributes('class'), undefined);
|
||||
input.element.value = '12';
|
||||
await wrapper.setProps({ disabled: true });
|
||||
assert.equal(input.element.value, '12');
|
||||
await wrapper.setProps({ unstyled: false });
|
||||
assert.equal(input.attributes('class'), 'gev-quantitative-slider');
|
||||
wrapper.unmount();
|
||||
});
|
||||
Reference in new issue
Block a user