refactor range input to composition api

This commit is contained in:
syuilo 2022-06-30 12:48:42 +09:00 committed by Johann150
parent e47af45407
commit f5d87fbc9e
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1

View file

@ -16,162 +16,126 @@
</div>
</template>
<script lang="ts">
import { computed, defineAsyncComponent, defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
<script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, watch } from 'vue';
import * as os from '@/os';
export default defineComponent({
props: {
modelValue: {
type: Number,
required: false,
default: 0,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
min: {
type: Number,
required: false,
default: 0,
},
max: {
type: Number,
required: false,
default: 100,
},
step: {
type: Number,
required: false,
default: 1,
},
autofocus: {
type: Boolean,
required: false,
},
textConverter: {
type: Function,
required: false,
default: (v) => v.toString(),
},
},
setup(props, context) {
const containerEl = ref<HTMLElement>();
const thumbEl = ref<HTMLElement>();
const rawValue = ref((props.modelValue - props.min) / (props.max - props.min));
const steppedRawValue = computed(() => {
if (props.step) {
const step = props.step / (props.max - props.min);
return (step * Math.round(rawValue.value / step));
} else {
return rawValue.value;
}
});
const finalValue = computed(() => {
if (Number.isInteger(props.step)) {
return Math.round((steppedRawValue.value * (props.max - props.min)) + props.min);
} else {
return (steppedRawValue.value * (props.max - props.min)) + props.min;
}
});
const thumbWidth = computed(() => {
if (thumbEl.value == null) return 0;
return thumbEl.value!.offsetWidth;
});
const thumbPosition = ref(0);
const calcThumbPosition = () => {
if (containerEl.value == null) {
thumbPosition.value = 0;
} else {
thumbPosition.value = (containerEl.value.offsetWidth - thumbWidth.value) * steppedRawValue.value;
}
};
watch([steppedRawValue, containerEl], calcThumbPosition);
let ro: ResizeObserver | undefined;
onMounted(() => {
ro = new ResizeObserver((entries, observer) => {
calcThumbPosition();
});
ro.observe(containerEl.value);
});
onUnmounted(() => {
if (ro) ro.disconnect();
});
const steps = computed(() => {
if (props.step) {
return (props.max - props.min) / props.step;
} else {
return 0;
}
});
const onMousedown = (ev: MouseEvent | TouchEvent) => {
ev.preventDefault();
const tooltipShowing = ref(true);
os.popup(defineAsyncComponent(() => import('@/components/ui/tooltip.vue')), {
showing: tooltipShowing,
text: computed(() => {
return props.textConverter(finalValue.value);
}),
targetElement: thumbEl,
}, {}, 'closed');
const style = document.createElement('style');
style.appendChild(document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }'));
document.head.appendChild(style);
const onDrag = (ev: MouseEvent | TouchEvent) => {
ev.preventDefault();
const containerRect = containerEl.value!.getBoundingClientRect();
const pointerX = ev.touches && ev.touches.length > 0 ? ev.touches[0].clientX : ev.clientX;
const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth.value / 2));
rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth.value)));
};
let beforeValue = finalValue.value;
const onMouseup = () => {
document.head.removeChild(style);
tooltipShowing.value = false;
window.removeEventListener('mousemove', onDrag);
window.removeEventListener('touchmove', onDrag);
window.removeEventListener('mouseup', onMouseup);
window.removeEventListener('touchend', onMouseup);
//
if (beforeValue !== finalValue.value) {
context.emit('update:modelValue', finalValue.value);
}
};
window.addEventListener('mousemove', onDrag);
window.addEventListener('touchmove', onDrag);
window.addEventListener('mouseup', onMouseup, { once: true });
window.addEventListener('touchend', onMouseup, { once: true });
};
return {
rawValue,
finalValue,
steppedRawValue,
onMousedown,
containerEl,
thumbEl,
thumbPosition,
steps,
};
},
const props = withDefaults(defineProps<{
modelValue: number;
disabled?: boolean;
min: number;
max: number;
step?: number;
textConverter?: (value: number) => string,
}>(), {
step: 1,
textConverter: (v) => v.toString(),
});
const emit = defineEmits<{
(ev: 'update:modelValue', value: number): void;
}>();
const containerEl = ref<HTMLElement>();
const thumbEl = ref<HTMLElement>();
const rawValue = ref((props.modelValue - props.min) / (props.max - props.min));
const steppedRawValue = computed(() => {
if (props.step) {
const step = props.step / (props.max - props.min);
return (step * Math.round(rawValue.value / step));
} else {
return rawValue.value;
}
});
const finalValue = computed(() => {
if (Number.isInteger(props.step)) {
return Math.round((steppedRawValue.value * (props.max - props.min)) + props.min);
} else {
return (steppedRawValue.value * (props.max - props.min)) + props.min;
}
});
const thumbWidth = computed(() => {
if (thumbEl.value == null) return 0;
return thumbEl.value!.offsetWidth;
});
const thumbPosition = ref(0);
const calcThumbPosition = () => {
if (containerEl.value == null) {
thumbPosition.value = 0;
} else {
thumbPosition.value = (containerEl.value.offsetWidth - thumbWidth.value) * steppedRawValue.value;
}
};
watch([steppedRawValue, containerEl], calcThumbPosition);
let ro: ResizeObserver | undefined;
onMounted(() => {
ro = new ResizeObserver((entries, observer) => {
calcThumbPosition();
});
ro.observe(containerEl.value);
});
onUnmounted(() => {
if (ro) ro.disconnect();
});
const steps = computed(() => {
if (props.step) {
return (props.max - props.min) / props.step;
} else {
return 0;
}
});
const onMousedown = (ev: MouseEvent | TouchEvent) => {
ev.preventDefault();
const tooltipShowing = ref(true);
os.popup(defineAsyncComponent(() => import('@/components/ui/tooltip.vue')), {
showing: tooltipShowing,
text: computed(() => {
return props.textConverter(finalValue.value);
}),
targetElement: thumbEl,
}, {}, 'closed');
const style = document.createElement('style');
style.appendChild(document.createTextNode('* { cursor: grabbing !important; } body * { pointer-events: none !important; }'));
document.head.appendChild(style);
const onDrag = (ev: MouseEvent | TouchEvent) => {
ev.preventDefault();
const containerRect = containerEl.value!.getBoundingClientRect();
const pointerX = ev.touches && ev.touches.length > 0 ? ev.touches[0].clientX : ev.clientX;
const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth.value / 2));
rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth.value)));
};
let beforeValue = finalValue.value;
const onMouseup = () => {
document.head.removeChild(style);
tooltipShowing.value = false;
window.removeEventListener('mousemove', onDrag);
window.removeEventListener('touchmove', onDrag);
window.removeEventListener('mouseup', onMouseup);
window.removeEventListener('touchend', onMouseup);
//
if (beforeValue !== finalValue.value) {
emit('update:modelValue', finalValue.value);
}
};
window.addEventListener('mousemove', onDrag);
window.addEventListener('touchmove', onDrag);
window.addEventListener('mouseup', onMouseup, { once: true });
window.addEventListener('touchend', onMouseup, { once: true });
};
</script>
<style lang="scss" scoped>