InspectorNumber.vue 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. <script setup lang="ts">
  2. import { watch } from 'vue'
  3. const props = defineProps<{
  4. label: string
  5. value: any
  6. type?: string
  7. path?: string
  8. min?: number
  9. max?: number
  10. step?: number
  11. }>()
  12. const emit = defineEmits(['change'])
  13. function onChange(event: Event) {
  14. const { target } = event
  15. emit('change', (target as HTMLInputElement).valueAsNumber)
  16. }
  17. function onKeydown(event: KeyboardEvent) {
  18. if (event.code === 'Space' || event.code === 'Enter') {
  19. event.preventDefault() // Prevent scrolling when space is pressed
  20. emit('change', !props.value)
  21. }
  22. }
  23. const mouse = useMouse()
  24. const initialMouseX = ref(0)
  25. const isMouseDown = ref(false)
  26. const onInputMouseDown = (event: MouseEvent) => {
  27. initialMouseX.value = event.clientX
  28. isMouseDown.value = true
  29. }
  30. const onInputMouseUp = () => {
  31. isMouseDown.value = false
  32. }
  33. const calculateSpeed = (diff: number) => Math.floor(Math.abs(diff) / 10)
  34. watch(mouse.x, (newValue) => {
  35. if (isMouseDown.value) {
  36. const diff = newValue - initialMouseX.value
  37. const speed = calculateSpeed(diff)
  38. if (diff > 0) {
  39. emit('change', props.value + 1 + speed)
  40. }
  41. else if (diff < 0) {
  42. emit('change', props.value - 1 + speed)
  43. }
  44. if (props.min !== undefined && props.value < props.min) {
  45. emit('change', props.min)
  46. }
  47. if (props.max !== undefined && props.value > props.max) {
  48. emit('change', props.max)
  49. }
  50. initialMouseX.value = newValue
  51. }
  52. })
  53. </script>
  54. <template>
  55. <div class="flex justify-start gap-2 items-center">
  56. <input
  57. :id="path"
  58. :value="value"
  59. :min="min"
  60. :max="max"
  61. :step="step"
  62. class="
  63. bg-gray-50
  64. px-2 py-1
  65. text-xs
  66. font-mono
  67. rounded
  68. text-right
  69. focus:border-gray-200
  70. outline-none
  71. border-none
  72. w-1/3
  73. w-max-content
  74. "
  75. type="number"
  76. :class="{ 'cursor-ew-resize': isMouseDown }"
  77. @input="onChange"
  78. @mousedown="onInputMouseDown"
  79. @mouseup="onInputMouseUp"
  80. >
  81. </div>
  82. </template>